I am trying to make a writer class, which would be used to turn data into a binary stream, at compile-time using a std::vector, and then converting it to std::array to be used at runtime. eg:
#include <cstdint>
#include <array>
#include <vector>
class Writter
{
private:
std::vector<uint8_t> vec;
public:
constexpr Writter() {}
constexpr const std::vector<uint8_t> getVector() const { return (vec); }
constexpr void write(uint8_t val) { vec.push_back(val); }
constexpr size_t getVecSize() const { return (vec.size()); }
};
consteval Writter toConstant()
{
Writter wr;
//wr.write(1);
return(wr);
}
consteval auto getBinary()
{
constexpr Writter wr = toConstant();
constexpr std::vector<uint8_t> vec = wr.getVector();
constexpr size_t vecSize = vec.size();
std::array<uint8_t, vecSize> arr;
for(size_t i = 0; i < vecSize; ++i)
{
arr[i] = vec[i];
}
return arr;
}
int main()
{
constexpr auto binary = getBinary();
}
The current version compiles, but as soon as you try to use the wr.write(1) member function, compilation fails with the given output:
/opt/compiler-explorer/gcc-trunk-20240725/include/c++/15.0.0/bits/allocator.h:193:52: error: 'toConstant()()' is not a constant expression because it refers to a result of 'operator new'
193 | return static_cast<_Tp*>(::operator new(__n));
| ~~~~~~~~~~~~~~^~~~~
<source>:39:12: error: invalid types 'int[size_t {aka long unsigned int}]' for array subscript
39 | arr[i] = vec[i];
I have tried to use different containers, or just having a raw pointer and managing the memory myself, but the problem always seems to be that the compiler doesnt understand that the std::vector member variable (or any other container), can be evaluated at compile time. Is there any way to make the compiler understand that it is within a constexpr context?
Edit: there are two similar question posted, but they are not quite what is being asked here, the questions and differences are;
you need to wait for c++20
).Where as in this question, C++20 is being used, thus std::vector is supported in the context of constexpr. And the memory allocated is also free'd at compile time.
So the question rather is; How to make the data of an std::vector persist at runtime using an std::array?
Bringing the very helpful comments of user17732522 to light;
The problem lies in the fact that std::vector
, even though it can be used within a constexpr context, it cannot, itself, be declared as constexpr, nor a class that contains it. (same goes for std::string
and anything utilizing dynamic memory allocation)
Thus, the toConstant()
function has to be called twice, once for getting the vector (not as constexpr) and once for the size of the vector (as constexpr).
The class itself is correct, only getBinary()
has to change to something like this:
consteval auto getBinary()
{
std::vector<uint8_t> vec = toConstant().getVector();
constexpr size_t vecSize = toConstant().getVector().size();
std::array<uint8_t, vecSize> arr;
for(size_t i = 0; i < vecSize; ++i)
{
arr[i] = vec[i];
}
return arr;
}
Thanks again to user17732522, and please, if I explained something wrong, drop a comment.