Reputation: 1371
I managed to solve a previous question about initializing a static char array, asked here: Initializing a static char based on template parameter
I don't like the need for a secondary function in my solution:
//static char arr[N] = {[0]='0', [1]='x', [N-1]='\0',};
// ideally want this, but not currently implemented in g++
template <char... chars>
struct zero_str {};
template <unsigned N, char... chars>
struct seq_gen { using type = typename seq_gen<N-1, '0', chars...>::type; };
template <char... chars>
struct seq_gen<0, chars...> { using type = zero_str<chars...>; };
template <size_t N>
struct zero_gen { using type = typename seq_gen<N-1, '0', '\0'>::type; };
template <>
struct zero_gen<0> { using type = zero_str<'\0'>; };
template<size_t N> using strsize = typename zero_gen<N>::type;
template<typename T, char... chars>
const char* n2hexHelper(T val, zero_str<chars...>)
{
thread_local static char hexstr[] = {'0', 'x', chars...};
/* convert to hex */
return hexstr;
};
template<typename T>
const char* n2hex(T val)
{
return n2hexHelper<T> (val, strsize<sizeof(T)*2>() );
}
int main()
{
std::cout << n2hex(1) << std::endl;
return EXIT_SUCCESS;
}
Instead, I'd prefer not to need the dummy variable passed to the helper function, and be able to do something like this:
template<typename T, char... chars>
const char* n2HexIdeal(T val)
{
thread_local static char hexstr[] = {'0', 'x', chars...}; //how to get chars... ?
/* convert to hex */
return hexstr;
}
I have two main questions. 1) is something like my ideal case possible with parameter pack expansions? Or is the only way to force the compiler to deduce my char... is to use it as a function parameter? 2) I'm not very familiar with template metaprogramming, so I was wondering if there are any glaring faults or idiomatic missteps with my above solution.
Upvotes: 1
Views: 129
Reputation: 275510
This is doable in C++14.
template<class=void, std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto) {
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto index_over( std::integral_constant< std::size_t, N > ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
These are two helper functions that allow you to expand a bunch of std::size_t
compile-time values without a "custom" helper function at each point of use.
We can then use them:
template<typename T>
const char * toStr(T num)
{
thread_local static
auto str = index_over<sizeof(T)*3>()
([&](auto...Is)->std::array<char, 3+3*sizeof(T)> {
return {{ '0', 'x',
(void(Is),'0')...,
'\0'
}};
});
// do something with str
(void)num;
return str.data();
}
to generate and expand our parameter packs inline.
This requires auto variardic lambdas, which is why it doesn't work in C++11.
Upvotes: 1
Reputation: 66210
If you can accept that your helper function is a static method of a class/struct, you can use the partial specialization as follows
template <typename, typename>
struct n2hexH;
template <typename T, char ... Chs>
struct n2hexH<T, zero_str<Chs...>>
{
static char * func (T const & val)
{
thread_local static char hexstr[] = {'0', 'x', Chs...};
/* convert to hex */
return hexstr;
}
};
Now, your n2hex()
function become
template<typename T>
const char* n2hex(T val)
{ return n2hexH<T, strsize<(sizeof(T)<<1U)>>::func(val); }
or you can tranform it in a macro
#define n2hex(X) n2hexH<decltype(X), strsize<(sizeof(X)<<1U)>>::func(X)
Upvotes: 1