How to initialize a constexpr std::array with templated constexpr member functions? - c++

This is a follow up of my question given here. In the end I want to create a constexpr std::array containing text with an appended running index.
I wanted to try a different approach than in the previous question.
Nearly everything, what I do in the below code is constexpr. But maybe, it is simply the old problem of returning a pointer to a no longer existing variable. But, I doubt this.
Please see the following code, where the not working line in function main is marked.
#include <iostream>
#include <algorithm>
#include <iterator>
#include <array>
#include <string>
// Some example text
static constexpr const char BaseString[]{ "text" };
// To create something like "text123" as constexpr
template <const size_t numberToConvert, const char* Text>
class Converter {
public:
// Some helper variables
static constexpr size_t TextLength{ std::char_traits<char>::length(Text) };
static constexpr size_t NumberOfDigits{ ([]() constexpr noexcept {size_t result = 0; int temp = numberToConvert; for (; temp != 0; temp /= 10) ++result; return result; }()) };
static constexpr size_t ArrayLength{ (numberToConvert ? 1u : 2u) + NumberOfDigits + TextLength };
// Here we will build the text
char buf[ArrayLength]{};
// Constructor: Convert number to character digits
constexpr Converter() noexcept {
size_t i{ 0 }; for (; i < TextLength; ++i) buf[i] = Text[i]; // Copy text
if (numberToConvert == 0) buf[i] = '0';
else {
i = NumberOfDigits + TextLength - 1; // Convert number to character digits
int number = numberToConvert; for (; number; number /= 10)
buf[i--] = number % 10 + '0';
}
}
// cast operator
constexpr operator const char* () const noexcept { return buf; }
// For test purposes
constexpr const char* data() const noexcept { return buf; }
};
// Driver program
int main() {
// Temporaray constexprs
constexpr Converter<123, BaseString> conv123{}; // Default construction
constexpr auto conv2 = Converter<2, BaseString>(); // Assign / copy
// Build constexpr std::array and initialize it with constexprs
constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
// Show that it works
std::copy(convArray1.begin(), convArray1.end(), std::ostream_iterator<const char*>(std::cout, "\n"));
// Does compile, but not work. Array will be initialized with nullptr *******************************************
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
std::cout << convArray2[0] << '\n' << convArray2[0] << '\n';
return 0;
}
So, I can create constexpr "values" with my templated class. Those values can be used in the "initializer" list for a constexpr std::array. But, if I want to use my class directly in the initializer list, then it compiles, but stores only nullptrs. Output of the program is:
text123
text2
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
Why does this happen? Or, is there a solution?
Compiled with Microsoft Visual Studio Community 2019, Version 16.8.2, C++17, Debug, X86

Your code generating compile time dangling pointers (which should be impossible) on MSVC.
To fix:
template <const size_t numberToConvert, const char* Text>
class Converter {
// blah
std::array<char, ArrayLength> buf{};
constexpr operator std::array<char, ArrayLength>() const { return buf; }
constexpr std::array<char, ArrayLength> get() const { return *this; }
};
and remove other conversion operators and data method.
template<const size_t numberToConvert, const char* Text>
constexpr auto Converted = Converter<numberToConvert, Text>{}.get();
and now use Converted<blah...>.data() to get the pointers you want.
If you really want implicit conversion to character pointer:
template<const size_t numberToConvert, const char* Text>
struct Convertest {
constexpr operator char const*() const { return Converted<numberToConvert,Text>.data(); }
};
rename classes and variables however you like.

constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(),
Converter<2, BaseString>().data() };
Here, you are storing pointers to temporary variables - both Converter objects seize to exist after ;. Making dereferencing the pointers UB.
Clang rejects such code giving quite helpful message:
<source>:51:43: note: pointer to subobject of temporary is not a constant expression
<source>:51:55: note: temporary created here
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
^
2 errors generated.
Execution build compiler returned: 1
I am not sure about the specific constexpr rules but the code is unsafe even if it would compile.

In Cpp-Reference you can see that
A constant expression is either
[...]
a prvalue core constant expression whose value satisfies the following constraints: [...] if the value is of pointer type, it holds - address of an object with static storage duration
So, for convArray1
constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
you have to make static conv123 and conv2
// VVVVVV
static constexpr Converter<123, BaseString> conv123{};
static constexpr auto conv2 = Converter<2, BaseString>();
// ^^^^^^
because you can't have a constant expression from a pointer with not static storage.
For convArray2
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
I don't see a way to get a constexpr object from pointers inside temporary objects.

Related

c++ constexpr concatenate char*

Context:
In my company we generate a lot of types based on IDL files. Some of the types require special logic so they are handcoded but follow the same pattern as the generated ones. We have a function which all types must implement which is a name function. This will return the type name as a char* string and the function is constexpr.
Problem:
The problem is regarding collections which could contain other collections nested potentially N number of times. I therefore am trying to concatenate two or more char* strings at compile time.
Pseudocode of what I want to achieve:
template <typename T>
constexpr char* name()
{
constexpr char* collectionName = "std::vector";
constexpr char* containedTypeName = name<T>();
return concat(collectionName, "<", containedTypeName, ">");
}
Note:
There are examples out there which does something like this but is done with char[] or the use of static variables.
The question:
How can I make a constexpr function which return a char* which consists of two or more concatenated char* strings at compile time? I am bound to C++17.
From constexpr you cannot return char* which is constructed there... You must return some compile time known(also its size) constant thingy.
A possible solution could be something like:
#include <cstring>
// Buffer to hold the result
struct NameBuffer
{
// Hardcoded 128 bytes!!!!! Carefully choose the size!
char data[128];
};
// Copy src to dest, and return the number of copied characters
// You have to implement it since std::strcpy is not constexpr, no big deal.
constexpr int constexpr_strcpy(char* dest, const char* src);
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr NameBuffer name()
{
// We will return this
NameBuffer buf{};
constexpr const char* collectionName = "std::vector";
constexpr const char* containedTypeName = "dummy";
// Copy them one by one after each other
int n = constexpr_strcpy(buf.data, collectionName);
n += constexpr_strcpy(buf.data + n, "<");
n += constexpr_strcpy(buf.data + n, containedTypeName);
n += constexpr_strcpy(buf.data + n, ">");
// Null terminate the buffer, or you can store the size there or whatever you want
buf.data[n] = '\0';
return buf;
}
Demo
And since the returned char* is only depends on the template parameter in your case, you can create templated variables, and create a char* to them, and it can act like any other char*...
EDIT:
I have just realized that your pseudo code will never work!! Inside name<T>() you are trying to call name<T>().
You must redesign this!!! But! With some hack you can determine the size at compile time somehow for example like this:
#include <cstring>
#include <iostream>
template<std::size_t S>
struct NameBuffer
{
char data[S];
};
// Copy src to dest, and return the number of copied characters
constexpr int constexpr_strcpy(char* dest, const char* src)
{
int n = 0;
while((*(dest++) = *(src++))){ n++; }
return n;
}
// Returns the len of str without the null term
constexpr int constexpr_strlen(const char* str)
{
int n = 0;
while(*str) { str++; n++; }
return n;
}
// This template parameter does nothing now...
// I left it there so you can see how to create the template variable stuff...
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr auto createName()
{
constexpr const char* collectionName = "std::vector";
constexpr const char* containedTypeName = "dummy";
constexpr std::size_t buff_size = constexpr_strlen(collectionName) +
constexpr_strlen(containedTypeName) +
2; // +1 for <, +1 for >
/// +1 for the nullterm
NameBuffer<buff_size + 1> buf{};
/// I'm lazy to rewrite, but now we already calculated the lengths...
int n = constexpr_strcpy(buf.data, collectionName);
n += constexpr_strcpy(buf.data + n, "<");
n += constexpr_strcpy(buf.data + n, containedTypeName);
n += constexpr_strcpy(buf.data + n, ">");
buf.data[n] = '\0';
return buf;
}
// Create the buffer for T
template<typename T>
static constexpr auto name_buff_ = createName<T>();
// point to the buffer of type T. It can be a function too as you wish
template<typename T>
static constexpr const char* name = name_buff_<T>.data;
int main()
{
// int is redundant now, but this is how you could use this
std::cout << name<int> << '\n';
return 0;
}
Demo

Compiletime narrowing wchar to char

In my compile time function I'd like to work with strings. BOTH ANSI and WIDE ones. So, I added a quick template to handle both. This is all easy-peasy, but I've got a special function which calculates security checksum on strings. This works on a byte array and it would take quite huge effort to rewrite to work on variable buffer size, so I thought I will just simply narrow the wchar down to char and let my function work on it. By default it doesn't work as I thought it should be.
Sample code to reproduce my problem:
https://godbolt.org/z/ya2zq7
#include <iostream>
constexpr void hack(const char* const from, const size_t fromLen, char* const to)
{
for (size_t i = 0; i < fromLen; i++)
{
to[i] = from[i] + 1;
}
}
template <typename U, std::size_t LENGTH>
class EncryptedStorage
{
U m_data[LENGTH]{};
public:
constexpr EncryptedStorage(const U* input)
{
hack(static_cast<const char* const>(input), LENGTH * sizeof(U), static_cast<char* const>(m_data));
}
};
int main()
{
// Test with CHAR
constexpr char test[] = "Hello World";
constexpr size_t size = sizeof(test) / sizeof(test[0]);
constexpr auto encrypted = EncryptedStorage<char, size>(test);
// test with WCHAR
constexpr wchar_t wtest[] = L"Hello World";
constexpr size_t wsize = sizeof(wtest) / sizeof(wtest[0]);
constexpr auto wencrypted = EncryptedStorage<wchar_t, wsize>(wtest);
}
If you comment the wide strings it will compile perfectly. Is it possible to do what I want, or I should really really rework all my algorithm to work on variable size?
The basic problem in your code is that you can't use static_cast to covert between pointers to different data types - when those types are unrelated, as char and wchar_t are; for this, you need a reinterpret_cast or a C-style cast:
constexpr EncryptedStorage(const U* input)
{
hack(reinterpret_cast<const char* const>(input), LENGTH * sizeof(U), reinterpret_cast<char* const>(m_data));
}
However, once you have such a cast, your EncyptedStorage function can no longer be evaluated at compile time, so two of theconstexpr declarations in your main will fail, and you will have to just use const instead:
const auto encrypted = EncryptedStorage<char, size>(test);
const auto wencrypted = EncryptedStorage<wchar_t, wsize>(wtest); // Can't use constexpr
EDIT:
Another way (perhaps nicer) is to use function-style casts:
using pcchar = const char* const;
using pchar = char* const;
constexpr EncryptedStorage(const U* input)
{
hack(pcchar(input), LENGTH * sizeof(U), pchar(m_data));
}
With this, you can use constexpr for encrypted but not for wencrypted!

Initialize array of compile time defined size as constant expression

I have an array of strings that must be allocated once and their underlying c_str must remain valid for entire duration of the program.
There's some API that provides info about some arbitrary data types. Could look like this:
// Defined outside my code
#define NUMBER_OF_TYPES 23
const char* getTypeSuffix(int index);
The getTypeSuffix is not constexpr, so this must work at least partially in runtime.
The interface that I must provide:
// Returned pointer must statically allocated (not on stack, not malloc)
const char* getReadableTypeName(int type);
Now my array should have following type:
std::string typeNames[NUMBER_OF_TYPES];
For my purposes, it will be initialized within a wrapper class, right in the constructor:
class MyNames
{
MyNames()
{
for (int i = 0; i < NUMBER_OF_TYPES; ++i)
{
names[i] = std::string("Type ") + getTypeSuffix(i);
}
}
const char* operator[](int type) { return _names[(int)type].c_str(); }
private:
std::string _names[NUMBER_OF_TYPES];
};
This is then used in an singleton-ish kind of way, for example:
const char* getReadableTypeName(int type)
{
static MyNames names;
return names[type];
}
Now what I want to improve is that I can see that the for loop in the constructor could be replaced as such:
MyNames() : _names{std::string("Type ") + getTypeSuffix(0), std::string("Type ") + getTypeSuffix(1), ... , std::string("Type ") + getTypeSuffix(NUMBER_OF_TYPES-1)}
{}
Obviously a pseudocode, but you get the point - the array can be initialized directly, leaving the constructor without body, which is neat. It also means that the array member _names can be const, further enforcing the correct usage of this helper class.
I'm quite sure there would be many other uses to filling an array by expressions in compile time, instead of having loop. I would even suspect that this is something that happens anyway during 03.
Is there a way to write a C++11 style array initializer list that has flexible length and is defined by an expression? Another simple example would be:
constexpr int numberCount = 10;
std::string numbers[] = {std::to_string(1), std::to_string(2), ... , std::to_string(numberCount)};
Again, an expression instead of a loop.
I'm not asking this question because I was trying to drastically improve performance, but because I want to learn about new, neat, features of C++14 and later.
instead of C-array use std::array, then you might write your function to return that std::array and your member can then be const:
std::array<std::string, NUMBER_OF_TYPES> build_names()
{
std::array<std::string, NUMBER_OF_TYPES> names;
for (int i = 0; i < NUMBER_OF_TYPES; ++i)
{
names[i] = std::string("Type ") + getTypeSuffix(i);
}
return names;
}
class MyNames
{
MyNames() : _names(build_names()) {}
const char* operator[](int type) const { return _names[(int)type].c_str(); }
private:
const std::array<std::string, NUMBER_OF_TYPES> _names;
};
Now you have std::array, you might use variadic template instead of loop, something like (std::index_sequence stuff is C++14, but can be implemented in C++11):
template <std::size_t ... Is>
std::array<std::string, sizeof...(Is)> build_names(std::index_sequence<Is...>)
{
return {{ std::string("Type ") + getTypeSuffix(i) }};
}
and then call it:
MyNames() : _names(build_names(std::make_index_sequence<NUMBER_OF_TYPES>())) {}
You can defer to an initialization function:
std::array<std::string, NUMBER_OF_TYPES> initializeNames()
{
std::array<std::string, NUMBER_OF_TYPES> names;
for (int i = 0; i < NUMBER_OF_TYPES; ++i) {
names[i] = std::string("Type ") + getTypeSuffix(i);
}
return names;
}
const char* getReadableTypeName(int type)
{
static auto const names = initializeNames();
return names[type].c_str();
}
which can be an immediately invoked lambda:
static auto const names = []{
std::array<std::string, NUMBER_OF_TYPES> names;
// ...
return names;
}();
or do you really need the array requirement? We're making strings anyway so I don't understand, then you can just use range-v3:
char const* getReadableTypeName(int type) {
static auto const names =
view::iota(0, NUMBER_OF_TYPES)
| view::transform([](int i){ return "Type "s + getTypeSuffix(i); })
| ranges::to<std::vector>();
return names[type].c_str():
}
You can use std::make_integer_sequence and a delegating constructor in C++14 (Implementations of std::make_integer_sequence exist in C++11, so this is not really C++14 specific) to get a template parameter pack of integers
#include <string>
#include <utility>
#define NUMBER_OF_TYPES 23
const char* getTypeSuffix(int index);
class MyNames
{
MyNames() : MyNames(std::make_integer_sequence<int, NUMBER_OF_TYPES>{}) {}
template<int... Indices>
MyNames(std::integer_sequence<int, Indices...>) : _names{ (std::string("Type ") + getTypeSuffix(Indices))... } {}
const char* operator[](int type) { return _names[(int)type].c_str(); }
private:
const std::string _names[NUMBER_OF_TYPES];
};
This means that no strings are being default constructed.
Since you ache to use new features, let's use range-v3 (soon-to-be the ranges library in C++2a) to write some really short code:
const char* getReadableTypeName(int type)
{
static const std::vector<std::string> names =
view::ints(0, 23) | view::transform([](int i) {
return "Type " + std::to_string(i);
});
return names[type].c_str();
}
https://godbolt.org/z/UVoENh

In constexpr function breakpoint is hit

Here is my code:
#include <string.h>
#include <stdlib.h>
template <int ...I>
class MetaString
{
char buffer_[sizeof...(I)+1];
public:
// A constexpr constructor
constexpr MetaString(const char * arg) :buffer_{ encrypt(arg[I])... }
{}
constexpr const char *get()const { return buffer_; }
private:
constexpr char encrypt(const char c) const { return c ^ 0x55; }
};
char *decrypt(const char* buffer_, int size)
{
char* tmp = (char *)malloc(size + 1);
strcpy_s(tmp, size + 10, buffer_);
for (int i = 0; i < size; i++)
{
*(tmp + i) = *(tmp + i) ^ 0x55;
}
return tmp;
}
int main()
{
constexpr MetaString<0,1,2,3,5> var("Post Malone");
char * var1 = decrypt(var.get(), 5);
std::cout << var1 << std::endl;
return 1;
}
The idea is simple, I create object of MetaString and provide some string to it. The constructor encrypts the argument by XOR. Then I have decrypt function which decrypts value back.
The problem is that I set breakpoint in constructor (specifically this line constexpr MetaString(const char * arg) :buffer_{ encrypt(arg[I])... }) and it is hit when I run in debugging mode. Which as I understand means that the constructor is called during runtime.
To guarantee that functions be evaluated at compile time I created object this way constexpr MetaString<0,1,2,3,5> var("Post Malone"); But I've read that constexpr variable must be literal type.
So my question is how can I manage to have variable like var (which would have encrypted data in it and be evaluated at compilation time) and then call decrypt at runtime and get original value?
constexpr only guarantees that a function or variable can be used in a constant expression. It does not guarantee that a function/object is going to always be evaluated/constructed at compiletime. In your particular case, that's actually not really possible since we're talking about an object with automatic storage duration. The object, if it is going to be created, can only really be created when the program is running. Try making your variable static…

Using "constexpr" to use string literal for template parameter

I have written some code to cast const char* to int by using constexpr and thus I can use a const char* as a template argument. Here is the code:
#include <iostream>
class conststr
{
public:
template<std::size_t N>
constexpr conststr(const char(&STR)[N])
:string(STR), size(N-1)
{}
constexpr conststr(const char* STR, std::size_t N)
:string(STR), size(N)
{}
constexpr char operator[](std::size_t n)
{
return n < size ? string[n] : 0;
}
constexpr std::size_t get_size()
{
return size;
}
constexpr const char* get_string()
{
return string;
}
//This method is related with Fowler–Noll–Vo hash function
constexpr unsigned hash(int n=0, unsigned h=2166136261)
{
return n == size ? h : hash(n+1,(h * 16777619) ^ (string[n]));
}
private:
const char* string;
std::size_t size;
};
// output function that requires a compile-time constant, for testing
template<int N> struct OUT
{
OUT() { std::cout << N << '\n'; }
};
int constexpr operator "" _const(const char* str, size_t sz)
{
return conststr(str,sz).hash();
}
int main()
{
OUT<"A dummy string"_const> out;
OUT<"A very long template parameter as a const char*"_const> out2;
}
In this example code, type of out is OUT<1494474505> and type of out2 is OUT<106227495>. Magic behind this code is conststr::hash() it is a constexpr recursion that uses FNV Hash function. And thus it creates an integral hash for const char* which is hopefully a unique one.
I have some questions about this method:
Is this a safe approach to use? Or can this approach be an evil in a specific use?
Can you write a better hash function that creates different integer for each string without being limited to a number of chars? (in my method, the length is long enough)
Can you write a code that implicitly casts const char* to int constexpr via conststr and thus we will not need aesthetically ugly (and also time consumer) _const user-defined string literal? For example OUT<"String"> will be legal (and cast "String" to integer).
Any help will be appreciated, thanks a lot.
Although your method is very interesting, it is not really a way to pass a string literal as a template argument. In fact, it is a generator of template argument based on string literal, which is not the same: you cannot retrieve string from hashed_string... It kinda defeats the whole interest of string literals in templates.
EDIT : the following was right when the hash used was the weighted sum of the letters, which is not the case after the edit of the OP.
You can also have problems with your hash function, as stated by mitchnull's answer. This may be another big problem with your method, the collisions. For example:
// Both outputs 3721
OUT<"0 silent"_const> out;
OUT<"7 listen"_const> out2;
As far as I know, you cannot pass a string literal in a template argument straightforwardly in the current standard. However, you can "fake" it. Here's what I use in general:
struct string_holder //
{ // All of this can be heavily optimized by
static const char* asString() // the compiler. It is also easy to generate
{ // with a macro.
return "Hello world!"; //
} //
}; //
Then, I pass the "fake string literal" via a type argument:
template<typename str>
struct out
{
out()
{
std::cout << str::asString() << "\n";
}
};
EDIT2: you said in the comments you used this to distinguish between several specializations of a class template. The method you showed is valid for that, but you can also use tags:
// tags
struct myTag {};
struct Long {};
struct Float {};
// class template
template<typename tag>
struct Integer
{
// ...
};
template<> struct Integer<Long> { /* ... */ };
// use
Integer<Long> ...; // those are 2
Integer<Float> ...; // different types
Here is the pattern that I am using for template const string parameters.
class F {
static constexpr const char conststr[]= "some const string";
TemplateObject<conststr> instance;
};
see :
https://stackoverflow.com/a/18031951/782168