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.
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.
live example.
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)
Related
I have following pseudo code that is trying to combine strings. Is there a way in C++ to expand a pack of char arrays(literal strings) into template parameter list of arrays and its index sequences?
template <char const *S0, size_t... I0, char const *S1, size_t... I1, ...>
struct combine_t
{
constexpr static char const val[] = {S0[I0]..., S1[I1]..., ..., 0};
};
template <size_t... L>
constexpr void combine_str(char const (&...strs)[L])
{
constexpr char const *v = combine_t<((strs, std::make_index_sequence<L>), ...)>::val;
}
You can turn your string literal into char sequences, and then "concatenate" them:
Following use gnu extension to transform literal sequence into char sequence, alternatively, you might use macro
MAKE_CHAR_SEQUENCE to have similar result.
template <char ... Cs> struct char_sequence
{
static constexpr const char data[] = {Cs..., 0};
};
// use gnu extension :/
template<class CharT, CharT... cs>
constexpr char_sequence<cs...> operator ""_seq(){
return {};
}
template <char ... Cs1, char ... Cs2>
constexpr char_sequence<Cs1..., Cs2...>
operator+(char_sequence<Cs1...>, char_sequence<Cs2...>) { return {}; }
and then
template <typename ... CharSeqs>
struct combine_t
{
constexpr static auto &val = (char_sequence<>{} + ... + CharSeqs{}).data;
};
I would like to initialize constexpr char[] member with another constexpr char [] member. Is it possible to do in C++11 or above?
#include <iostream>
struct Base {
static constexpr char ValueOne[] = "One";
static constexpr char ValueTwo[] = "Two";
};
template <typename T>
struct ValueOneHolder {
static constexpr char Value[] = T::ValueOne; // << How can one initialize this?
};
int main() {
std::cout << ValueOneHolder<Base>::Value << std::endl;
return 0;
}
I would like to initialize constexpr char[] member with another constexpr char [] member. Is it possible to do in C++11 or above?
Starting from C++14 you can use std::make_index_sequence and std::index_sequence.
If it's OK for you works in a ValueOneHolder specialization, you first can develop a constexpr function that, given a C-style array, return the size of the array
template <typename T, std::size_t N>
constexpr std::size_t getDim (T const (&)[N])
{ return N; }
Nest you can declare ValueOneHolder adding a second template parameter with a default value that is an index sequence corresponding to T::ValueOne
template <typename T,
typename = std::make_index_sequence<getDim(T::ValueOne)>>
struct ValueOneHolder;
and last the easy part: the partial specialization with initialization
template <typename T, std::size_t ... Is>
struct ValueOneHolder<T, std::index_sequence<Is...>>
{ static constexpr char Value[] = { T::ValueOne[Is] ... }; };
Don't forget the following line, outside the struct
template <typename T, std::size_t ... Is>
constexpr char ValueOneHolder<T, std::index_sequence<Is...>>::Value[];
The following is a full C++14 compiling example
#include <utility>
#include <iostream>
struct Base
{
static constexpr char ValueOne[] = "One";
static constexpr char ValueTwo[] = "Two";
};
template <typename T, std::size_t N>
constexpr std::size_t getDim (T const (&)[N])
{ return N; }
template <typename T,
typename = std::make_index_sequence<getDim(T::ValueOne)>>
struct ValueOneHolder;
template <typename T, std::size_t ... Is>
struct ValueOneHolder<T, std::index_sequence<Is...>>
{ static constexpr char Value[] = { T::ValueOne[Is] ... }; };
template <typename T, std::size_t ... Is>
constexpr char ValueOneHolder<T, std::index_sequence<Is...>>::Value[];
int main()
{
std::cout << ValueOneHolder<Base>::Value << std::endl;
}
If you want a C++11, you can develop a substitute for std::make_index_sequence and std::index_sequence.
In this particular example you may declare Value as the following:
template <typename T>
struct ValueOneHolder {
static constexpr auto Value = T::ValueOne; // << How can one initialize this?
};
Please note, GCC will fail to link this example unless you switch to -std=c++17 or add the folloing lines in a source file.
constexpr char Base::ValueOne[];
constexpr char Base::ValueTwo[];
With C++14 it is also possible to make a constexpr copy of a constexpr string (or its substring), as shown in example below:
template<typename CharT, size_t Size>
struct basic_cestring {
using value_type = CharT;
template<size_t... I> constexpr
basic_cestring(const char* str, index_sequence<I...>)
: _data{str[I]...} {}
inline constexpr operator const CharT* () const { return _data; }
const CharT _data[Size + 1];
};
template<size_t Size>
struct cestring : public basic_cestring<char, Size> {
using index = make_index_sequence<Size>;
constexpr cestring(const char* str)
: basic_cestring<char, Size>(str, index{}) {}
};
So, I'm attempting to mess with constexpr strings as one will do and really only have this thus far:
template<char... CS> struct text {
static constexpr char c_str[] = {CS...};
static constexpr int size = sizeof...(CS);
};
and so this compiles
text<'a','b','c'> t;
std::cout<< t.c_str <<std::endl;
and outputs 'abc' as expected.
What I'm wondering is if there's a non-convoluted way to do the reverse; have a function that returns a text type with the necessary char template arguments given a char array.
Not exactly what you asked... and a little convoluted, I suppose... but if you define a constexpr function to detect the length of a string
constexpr std::size_t strLen (char const * str, std::size_t len = 0U)
{ return *str ? strLen(++str, ++len) : len; }
and an helper struct that define the required type
template <char const *, typename>
struct foo_helper;
template <char const * Str, std::size_t ... Is>
struct foo_helper<Str, std::index_sequence<Is...>>
{ using type = text<Str[Is]...>; };
you can obtain your type passing the string to
template <char const * Str>
struct foo : public foo_helper<Str, std::make_index_sequence<strLen(Str)>>
{ };
Unfortunately you can't pass a string literal to it in this way
foo<"abc">::type
but you have to pass from a global variable
constexpr char abcVar[] = "abc";
and call foo using the global variable
foo<abcVar>::type
This solution uses std::index_sequence and std::make_index_sequence, available only starting from C++14, but isn't too difficult to write a substitute for they in C++11.
The following is a full working example
#include <utility>
#include <iostream>
#include <type_traits>
template <char ... CS>
struct text
{
static constexpr char c_str[] = {CS...};
static constexpr int size = sizeof...(CS);
};
constexpr std::size_t strLen (char const * str, std::size_t len = 0U)
{ return *str ? strLen(++str, ++len) : len; }
template <char const *, typename>
struct foo_helper;
template <char const * Str, std::size_t ... Is>
struct foo_helper<Str, std::index_sequence<Is...>>
{ using type = text<Str[Is]...>; };
template <char const * Str>
struct foo : public foo_helper<Str, std::make_index_sequence<strLen(Str)>>
{ };
constexpr char abcVar[] = "abc";
int main()
{
static_assert(std::is_same<foo<abcVar>::type,
text<'a', 'b', 'c'>>{}, "!");
}
Off Topic: I suggest to add an ending zero in c_str[]
static constexpr char c_str[] = {CS..., 0};
if you want use it as the c_str() method of std::string.
I'm trying to implement some kind of map (a tuple of pair) which use compile time string as key (first element of the pair). So I wanted to use this answer but there is a problem with my code : the string is inside a pair.
#include <type_traits>
#include <tuple>
namespace meta {
template < typename T >
struct CType { using type = T; };
namespace detail {
template <typename T>
struct typeid_t {
using type = typename std::remove_cv<
typename std::remove_reference<T>::type
>::type;
};
}
template <typename T>
constexpr decltype(auto) typeid_(T&&) {
return CType<typename detail::typeid_t<T>::type>{};
}
}
struct HashConstString {
using value_type = uint32_t;
static constexpr uint32_t hash(const char* str) {
return str[0];
}
};
template < typename T_Hash,
typename... T_Pairs >
class UniversalMap {
template < typename T_Pair >
using U_Pair = decltype(std::make_pair(
std::integral_constant<typename T_Hash::value_type, T_Hash::hash(std::get<0>(T_Pair{}))>{},
typename decltype(meta::typeid_(std::get<1>(T_Pair{})))::type {}
));
using U_Map = decltype(std::make_tuple(
U_Pair<T_Pairs>{}...
));
private:
U_Map m_map;
};
template < typename T_Hash,
typename... T_Pairs >
constexpr decltype(auto) make_UniversalMap(T_Hash hash, T_Pairs... pairs) {
(void)hash;
((void)pairs,...);
return UniversalMap<T_Hash, T_Pairs...>();
}
int main() {
constexpr auto hashValue = HashConstString::hash("Test");
constexpr auto map = make_UniversalMap(HashConstString{},
std::make_pair("Test", meta::CType<int>{})
);
}
Wandbox
So I don't know how to hash correctly the string when it's already inside the pair. Because std::get give me back a reference and it seems it's the reason why I have a dereferenced null pointer error.
Is there some "tricks" to get this work without having to compute the hash before creating the pair?
The problem is not with std::get but with the fact that you create a tuple of const char*. "Test" decays to const char* when passed as argument to make_pair. Unfortunately explicitly specifying the pair template parameters (e.g. std::pair<const char[5], int>) does not work because you can't create a std container of type array.
The rather awkward solution is to use std::array:
struct HashConstString
{
using value_type = uint32_t;
static constexpr uint32_t hash(const char *str) { return str[0]; }
// add this overload
template <std::size_t N>
static constexpr uint32_t hash(std::array<char, N> str) { return str[0]; }
};
and then call like this:
constexpr auto map = make_UniversalMap(HashConstString{},
std::make_pair(std::array<char, 5>{"Test"}, int{}));
To avoid specifying the size for std::array you can create a helper function:
template <std::size_t N> constexpr auto make_strarray(const char(&str)[N])
{
// unfortunately std::array<char, N>{str} does not work :(
std::array<char, N> arr{};
for (std::size_t i = 0; i < N; ++i)
arr[i] = str[i];
return arr;
}
Or since in C++20 it looks like std::copy will be made constexpr:
template <std::size_t N> constexpr auto make_strarray(const char(&str)[N])
{
std::array<char, N> arr{};
std::copy(str, str + N, arr.begin());
return arr;
}
I have the following minimal code fragment which compiles and executes as expected:
template < class T, T... Chrs>
struct mystring
{
static auto c_str()
{
static char str[] = {Chrs..., 0};
return str;
}
};
template <class T, T... Chrs>
auto operator""_t()
{
return mystring<T, Chrs...>();
}
int main()
{
auto x = "Hallo"_t ;
std::cout << x.c_str() << std::endl;
}
Question:
Is it possible to write the template mystring in a way that it excepts:
auto x = mystring<'a','b','c'>();
but also
auto x = mystring< 1,2,3> ();
or any other type.
I have no idea how to write something like ( pseudo code ):
template < T ... Chrs> // how to define T here?
struct mystring
{ }
Also the following is not allowed:
template <typename T, T ...Chrs >
struct mystring<T...Chrs> {};
The same way you're doing it right now. Except instead of using T, where T is a template parameter, just use char directly:
template <char... Chrs>
struct mystring
{
/* rest as before */
};
Of course this now only works for char and not wchar_t (but then again, so does the original)
You can generalize this by writing something like:
template <class T, T... Vals>
struct array { ... };
template <char... Chrs>
using mystring = array<char, Chrs...>;
In C++17, we'll have template auto which would let you write:
template <auto... Vals>
struct array { /* .. */ };
and then it's up to you to verify that all the Vals are the same type. Perhaps via:
template <auto Val, decltype(Val)... Vals>
struct array { /* .. */ };