This is a really important question to me as it's a bottleneck right now and I'm trying to investigate possible ways to solve my problem: I need to constexpr construct a std::function-like class that I am using that is quite simple. However, it's using aligned storage so that we can configure a pointer-sized number of captured elements. Let's call it Function.
https://github.com/fwsGonzo/libriscv/blob/master/lib/libriscv/util/function.hpp#L91
Specifically, I am using Function with up to 1 pointer captured. Usually "this". These functions are working wonderfully, and they will not compile if you try to capture too much.
The problem is that they have to be constructed at run-time, and there are so many of them that they are using around 3500 nanoseconds (3.5 micros), which is an eternity for my use case. I absolutely have to find a way to reduce this setup cost somehow, so the natural way to do that would be to investigate if I can construct them at compile-time.
I've been unable to do so and the compiler outright tells me that the constructor which uses placement new cannot be used in a constexpr context. This question tells the same story:
C++ constexpr in place aligned storage construction
You can see the problematic statement here: https://github.com/fwsGonzo/libriscv/blob/master/lib/libriscv/util/function.hpp#L148
template<typename Callable>
Function (Callable callable) noexcept
{
static_assert(sizeof(Callable) <= FunctionStorageSize,
"Callable too large (greater than FunctionStorageSize)");
static_assert(std::is_trivially_copy_constructible_v<Callable>,
"Callable not trivially copy constructible");
static_assert(std::is_trivially_destructible_v<Callable>,
"Callable not trivially destructible");
m_func_ptr = &trampoline<Callable>;
new(reinterpret_cast<Callable *>(m_storage.data)) Callable(callable);
}
I am using C++20 and I am open to suggestions on how to solve this. Given that these functions have a constant-sized capture storage with a single function pointer, is it possible to construct these at compile time somehow? No heap allocations should result from this.
Using C++20 and if you increased the constraint on the type Callable
to be trivially_copyable
you could use bit_cast
. You would also have to define a union containing a member of type aligned_storage <size, alignment>
for all possible object size.
Unfortunately, I don't think there is a constexpr implementation of bit_cast
yet.
A partial solution could be to declare a constexpr constructor if Callable
designates a pointer to object type:
template<typename Callable>
constexpr
Function (Callable * callable) noexcept
m_pointer {callable}
m_func_ptr = &trampoline <Callable>
{}
//declare the union
union {
void * m_pointer;
Storage m_storage;
};
//end an overload trampoline specialized for pointer to object.