Stringify Template Arguments

Stringify a template numeric argument

Unfortunately, you probably require a string literal to be given asm, which you can't build with templates.

You can use BOOST_PP_REPEAT_FROM_TO to do it easily with macros though:

#include <boost/preprocessor/repetition/repeat_from_to.hpp>

#define F_DEFINITION(_, maxRegNum, __) \
template<> void f<maxRegNum>(MuliInstr instr) { \
if (instr.regA == maxRegNum) asm volatile ("mov r4, r" #maxRegNum); \
f<maxRegNum - 1>(instr); \
}

BOOST_PP_REPEAT_FROM_TO(1, 30, F_DEFINITION, _)

Without boost, you can still avoid some repetition by using macros. This should be equivalent code if your regA is an integer member:

template<int maxRegNum> void f(MuliInstr instr) {
static_assert(maxRegNum <= 31, "maxRegNum > 31 not supported");
switch (instr.regA) {
#define F_CASE(n) \
case n: \
if (maxRegNum >= n) asm volatile ( "mov r4, r" #n ); \
break
#define F_CASE_8(a, b, c, d, e, f, g, h) F_CASE(a); F_CASE(b); F_CASE(c); F_CASE(d); F_CASE(e); F_CASE(f); F_CASE(g); F_CASE(h)
F_CASE_8(0, 1, 2, 3, 4, 5, 6, 7);
F_CASE_8(8, 9, 10, 11, 12, 13, 14, 15);
F_CASE_8(16, 17, 18, 19, 20, 21, 22, 23);
F_CASE_8(24, 25, 26, 27, 28, 29, 30, 31);
}
}

Stringified template argument

No. The closest thing you can have is typeid(T).name(). However, the result of this is unspecified, even an implementation which returned empty strings for all types would be conforming. For debugging purposes it often is sufficient, though.

Stringify and Unstringify template arguments

You don't have to specify Moo, just MooPack, because moo will deduce the argument for you. However, I'd suggest that you make MooPack a typedef or nested class (called Pack) inside Moo itself, in which case you can easily access it by typename X::Pack inside the template.

class Moo {
public:
class Pack {
...
};
};
template<typename T> typename T::Pack packify(T t) {
...
}

// usage

Moo m;
Moo::Pack p = packify(m);

Variadic template function to create string

The reason of the error is that, string literals ("I", " am ", " years ", "old") are arrays of constant chars (char const [N], for some N). You can intercept they as char const * but not as std::string.

A little off topic, I suppose, but I give you two suggestions:

(1) divide Stringify() in two function: the variadic one, public, that call a private one (toStr(), in my following example) to make conversion over singles arguments

(2) avoid recursion for the variadic version of Stringify() but simply use pack expansion.

I mean... you can write Stringify() as follows

  template <typename... Ts>
std::string Stringify (Ts const & ... vals)
{
using unused = int[];

(void)unused { 0, (mString += toStr(vals), 0)... };

return mString;
}

or, if you can use C++17, using template folding

  template <typename... Ts>
std::string Stringify (Ts const & ... vals)
{ return ((mString += toStr(vals)), ...); }

For toStr(), I propose a template version that uses std::to_string() but enabled only when the template T type isn't convertible to std::string

  template <typename T>
typename std::enable_if<
false == std::is_convertible<T, std::string>::value,
std::string>::type toStr (T const & val)
{ return std::to_string(val); }

and the non-template version that accept a std::string

  std::string toStr (std::string const & val)
{ return val; }

This way, if an argument is directly convertible to std::string (is std::string or another type that can be used to construct a std::string) the non-template version is called; otherwise is called the template one.

The following is a full compiling example

#include <iostream>
#include <type_traits>

class StringStream
{
private:
std::string mString;

template <typename T>
typename std::enable_if<
false == std::is_convertible<T, std::string>::value,
std::string>::type toStr (T const & val)
{ return std::to_string(val); }

std::string toStr (std::string const & val)
{ return val; }

public:
StringStream() = default;
~StringStream() = default;

template <typename... Ts>
std::string Stringify (Ts const & ... vals)
{
using unused = int[];

(void)unused { 0, (mString += toStr(vals), 0)... };

return mString;
}
};

int main ()
{
int age = 42;
StringStream ss;
std::cout << ss.Stringify("I", " am ", age, " years ", "old") << std::endl;
}

Template function where template argument type depends on function arguments

I would like to ensure that h and l are the same type (say T),

It's simple: you can impose they are of the same template type T, so if you call the function with values of different types, you get a compilation error because there is ambiguity deducing T.

So

template <typename U, typename T>
void bound_check (U v, T l, T h)
{ /* ... */ }

I would like to ensure that h and l are the same type (say T), and T to be at least timplicitly convertible to decltype(v).

You can check it with a static_assert() (see Nikos C. answer) or you can also SFINAE activate/deactivate the function (by example, in a C++11 way) as follows

template <typename U, typename T>
typename std::enable_if<std::is_convertible<T, U>::value>::type bound_check (U & v, T l, T h)
{ /* do something with v, l and h */ }

I made an (arguably more dangerous) assumption that all possible type Ts are signed

Using SFINAE, you can add the corresponding test

template <typename U, typename T>
typename std::enable_if<std::is_convertible<T, U>::value
&& std::is_signed<T>::value>::type bound_check (U & v, T l, T h)
{ /* do something with v, l and h */ }

Maybe you can also check that T is an integral type (std::is_integral): std::is_signed is true also for floating point types.

if there is some trick to do template bound_check(T& v) where type are deduced from arguments before explicit template arguments are parsed, as the title says.

No, as far I know.

Anyway, if possible, this way you loose the "I would like to ensure that h and l are the same type" check.

If wonder if there's a way that I can call the function as in bound_check<l, h>(v)

Starting from C++17, you can use auto for non-type template parameter.

So, before C++17 I suppose the answer is "no".

Starting from C++17, you could write something as

template <auto l, auto h, typename U>
std::enable_if_t<std::is_same_v<decltype(l), decltype(h)>
&& std::is_convertible_v<decltype(l), U>
&& std::is_integral_v<decltype(l)>
&& std::is_signed_v<decltype(l)>> bound_check (U & v)
{ /* do something with v, l and h */ }

Generate a string based upon a class template's typename?

No, that is not possible, at least not at that level. Macros are evaluated before compilation, that is, before template type parameters like the T in your example are evaluated. What you could do however is something like this:

#include <string>
#include <memory>
template <class T> struct TInfo;
template <class T> class MyClass;

#define TINFO(type) \
template <> struct TInfo<type> { \
static char const* getName() { \
return #type; \
} \
}; \
typedef MyClass<type> type##_Class; \
typedef std::unique_ptr<MyClass<type>> type##_UPtr;

template <class T>
class MyClass {
private:
std::string m_typeName;

public:
MyClass(std::string objName = "ObjectName")
: m_typeName(std::string(TInfo<T>::getName()) + "_" + objName)
{}

std::string const& getName() {
return m_typeName;
}
};

//usage:
#include <iostream>

TINFO(int);
int main()
{
int_UPtr pi(new int_Class());
std::cout << pi->getName() << '\n';
}

See http://ideone.com/3ivyqv

How to convert typename T to string in c++

There is no built-in mechanism for this.

typeid(T)::name() can give some info, but the standard does not mandate this string to be human-readable; just that it has to be distinct for each type. (E.x. Microsoft Visual C++ uses human-readable strings; GCC does not.)

You can build your own system though. For example, traits-based. Something like this:

// default implementation
template <typename T>
struct TypeName
{
static const char* Get()
{
return typeid(T).name();
}
};

// a specialization for each type of those you want to support
// and don't like the string returned by typeid
template <>
struct TypeName<int>
{
static const char* Get()
{
return "int";
}
};

// usage:
const char* name = TypeName<MyType>::Get();

stringify arbitrary number of variables

I'm not sure that I understood what you're trying to do. The code below tokenizes, at compile time, __VA_ARGS__. It does not check the syntax: it blindly replaces the white-space and commas with '\0', stores the start of identifiers in arg and the number of arguments in argc.

#include <iostream>

template < unsigned N > constexpr
unsigned countarg( const char( &s )[N], unsigned i = 0, unsigned c = 0 )
{
return
s[i] == '\0'
? i == 0
? 0
: c + 1
: s[i] == ','
? countarg( s, i + 1, c + 1 )
: countarg( s, i + 1, c );
}

template < unsigned N > constexpr
unsigned skipid( char( &s )[N], unsigned i = 0 )
{
return s[i] == '\0' || s[i] == ' ' || s[i] == '\t' || s[i] == ','
? i
: skipid( s, i + 1 );
}

template < unsigned N, unsigned M > constexpr
unsigned tokenize( char( &s )[N], const char*(&a)[M], unsigned i = 0, unsigned j = 0 )
{
return s[i] == '\0'
? i
: s[i] == ' ' || s[i] == '\t' || s[i] == ','
? ((s[i] = '\0'),
tokenize( s, a, ++i, j ))
: ((a[j] = s + i),
i = skipid( s, i ),
tokenize( s, a, i, ++j ));
}

#define TOKENIZEVA( ... ) char orig[] = #__VA_ARGS__; const unsigned argc = countarg(#__VA_ARGS__); const char* arg[argc]; tokenize( orig, arg );

#define PRINT( ... ) { TOKENIZEVA(__VA_ARGS__) for ( auto s : arg ) std::cout << s << std::endl; }

int main()
{
PRINT( first, second, third, fourth );
return 0;
}


Related Topics



Leave a reply



Submit