A way to get parameter pack from tuple / array? - c++

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.

Related

Definition of struct template value in constructor / member function

I'm working with a Pascal library that uses UCSD Strings. I created this template struct for making working with them easier:
template <std::size_t N>
struct DString {
unsigned Reference, Size = N;
char String[N];
};
#define MAKE_STRING(X) DString<sizeof(X)>{ 0x0FFFFFFFF, sizeof(X) - 1, X}
auto foo = MAKE_STRING("Foo");
I know it does not cover WideChar cases but the library does neither in the first place so I'm safe in that regard.
Anyway, my problem is that I don't want to use a macro and instead would like to create a constructor. So I was wondering if does C++ offers a possibility of implementing something like this pseudocode:
template <std::size_t N>
struct DString {
unsigned Reference, Size = N;
char String[N];
DString<sizeof(s)>(const char* s, unsigned r = 0x0FFFFFFFF):
String(s), Reference(r) {}
};
auto foo = DString("Foo");
Of course, it does not need to be a "constructor". It is just an example.
I also tried this:
template <std::size_t N>
struct DString {
unsigned Reference, Size = N;
char String[N];
inline static DString build(const char* X) {
return DString<sizeof(X)>{ 0x0FFFFFFFF, sizeof(X) - 1, X};
}
};
auto foo = DString::build("Foo");
But that in itself represents another issue. Because now I cannot reference the static function from DString without doing DString< size >::build(...);.
What can I do in this case?
If you can use at least C++17... using a delegate constructor and CTAD...
You can add in DString an additional template constructor (maybe private), otherwise you can't initialize, directly, a char[] with a char const *
template <std::size_t ... Is>
DString (std::index_sequence<Is...>, char const * s, unsigned r)
: Reference{r}, Size{N}, String{ s[Is]... }
{ }
Next you have to call the new constructor from the old one
DString (char const * s, unsigned r = 0x0FFFFFFFFu):
DString(std::make_index_sequence<N>{}, s, r)
{ }
Last, you have to add an explicit deduction guide (given that you want a DString<3> from a char const [4]
template <std::size_t N>
DString (char const (&)[N], unsigned = 0u) -> DString<N-1u>;
and the automatic deduction works.
The following is a full compiling example
#include <utility>
#include <iostream>
template <std::size_t N>
struct DString {
private:
template <std::size_t ... Is>
DString (std::index_sequence<Is...>, char const * s, unsigned r)
: Reference{r}, Size{N}, String{ s[Is]... }
{ }
public:
unsigned Reference, Size = N;
char String[N];
DString (char const * s, unsigned r = 0x0FFFFFFFFu):
DString(std::make_index_sequence<N>{}, s, r)
{ }
};
template <std::size_t N>
DString (char const (&)[N], unsigned = 0u) -> DString<N-1u>;
int main()
{
auto foo = DString{"Foo"};
}

How to expand a pack of character arrays into template parameters of character arrays and its index sequences?

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;
};

How to create compile-time constant string with prepended length byte?

I have some old code that uses #define to, um, define some string literals, e.g.
#define FredString "\004FRED"
That I would like to update to avoid using #define. Closest I get is something like the following:
static constexpr char* FSraw= "FRED";
static constexpr char FSlen= (char)(sizeof(FSraw) - 1);
static constexpr char* FredString= FSlen FSraw;
but the compiler seems unhappy on the third line.
What would be the best way to construct such a string at compile time? Obviously I could still explicitly encode the length, but also obviously, that's more error prone.
Looking for solutions for C++17 or earlier.
template <std::size_t N>
constexpr std::array<char, N+1> AddLenPrefix(const char (&str)[N])
{
std::array<char, N+1> ret{};
ret[0] = N-1; // Exclude the '\0'.
for (std::size_t i = 0; i < N; i++)
ret[i+1] = str[i];
return ret;
}
static constexpr auto FredString = AddLenPrefix("FRED");
You can also have something like:
#include <string_view>
#include <utility>
template <std::string_view const& S, typename>
struct prepend_length_impl;
template <std::string_view const& S, std::size_t ... Is>
struct prepend_length_impl<S, std::index_sequence<Is...>> {
static constexpr const char value[]{ (sizeof...(Is) + '0'), S[Is]..., 0 };
};
template <std::string_view const& S>
struct prepend_length {
static constexpr std::string_view value =
prepend_length_impl<S, std::make_index_sequence<S.size()>>::value;
};
and then use it like:
static constexpr std::string_view raw = "fred";
static constexpr std::string_view fred = prepend_length<raw>::value; // "4fred"
DEMO

C++11: Initialize constexpr char array with another constexpr char array

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{}) {}
};

Parameter pack expansion questions

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)