With all the new features of C++ (C++11 suffices I think), what prevents from having a std::minmax function that returns a pair of references.
In this way, if one feeds two modifable references, they can be modified. Is this opening a can of worms?
#include<functional>
// maybe all this options can be simplified
template<class T1, class T2> struct common;
template<class T> struct common<T, T>{using type = T;};
template<class T> struct common<T const&, T&>{using type = T const&;};
template<class T> struct common<T&, T const&>{using type = T const&;};
template<class T> struct common<T, T&>{using type = T const&;};
template<class T> struct common<T&, T>{using type = T const&;};
template<class T> struct common<T const&, T>{using type = T const&;};
template<class T> struct common<T, T const&>{using type = T const&;};
template<class T1, class T2, class Compare = std::less<>, class Ret = typename common<T1, T2>::type>
std::pair<Ret, Ret> minmax(T1&& a, T2&& b, Compare comp = {}){
return comp(b, a) ?
std::pair<Ret, Ret>(std::forward<T2>(b), std::forward<T1>(a))
: std::pair<Ret, Ret>(std::forward<T1>(a), std::forward<T2>(b));
}
Test:
#include<cassert>
int main(){
{
int a = 1;
int b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
small += 1;
assert(a == 2);
}{
int const a = 1;
int b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int a = 1;
int const b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int const a = 1;
int const b = 10;
auto& small = minmax(a, b).first;
assert(small == 1);
// small += 1; error small is const reference, because a was const
}{
int b = 10;
auto& small = minmax(int(1), b).first;
assert(small == 1);
// small += 1; error small is const reference, because first argument was const
}{
int a = 1;
auto& small = minmax(a, int(10)).first;
assert(small == 1);
// small += 1; error small is const reference, because second argument was const
}
{
int const a = 1;
auto& small = minmax(a, int(10)).first;
assert(small == 1);
// small += 1; error small is const reference, because both arguments are const
}
{
// auto& small = minmax(int(1), int(10)).first; // error, not clear why
auto const& small = minmax(int(1), int(10)).first; // ok
// auto small2 = minmax(int(1), int(10)).first; // also ok
assert(small == 1);
// small += 1; error small is const reference, because both arguments are const
}
}
There was a paper kind of along these lines a long time ago, by Howard Hinnant: N2199. Its very opening example demonstrates the precise problem you're trying to solve:
The function can not be used on the left hand side of an assignment:
int x = 1;
int y = 2;
std::min(x, y) = 3; // x == 3 desired, currently compile time error
It goes on to list as examples the frequently dangling reference problem, mixing types, and being useful with move-only types, and goes on to propose new versions of min and max that address all of these problems - it includes a very thorough implementation at the bottom (which is too long to paste here). Implementing minmax() based on that should be pretty straightforward:
template <class T, class U,
class R = typename min_max_return<T&&, U&&>::type>
inline
std::pair<R, R>
minmax(T&& a, U&& b)
{
if (b < a)
return {std::forward<U>(b), std::forward<T>(a)};
return {std::forward<T>(a), std::forward<U>(b)};
}
The paper was rejected at the time. It's possible it could come back though.
Being able to get back mutable references is nice, but being able to avoid dangling references is even nicer. Anonymously quoting from an example I saw recently:
template<typename T> T sign(T);
template <typename T>
inline auto frob(T x, T y) -> decltype(std::max(sign(x - y), T(0))) {
return std::max(sign(x - y), T(0));
}
This function has undefined behaviour for all inputs (the narrowest
contract possible?).
Note that your common implementation has this problem. These cases:
template<class T> struct common<T, T&>{using type = T const&;};
template<class T> struct common<T&, T>{using type = T const&;};
template<class T> struct common<T const&, T>{using type = T const&;};
template<class T> struct common<T, T const&>{using type = T const&;};
all dangle. What this means if I have:
int i = 4;
auto result = your_minmax(i, 5);
result here is a pair<int const&, int const&>, one of which is a reference to i and the other of which dangles. All of these cases have to do using type = T; in order to be safe.
Related
I want to create a generalized heap data structure, and facing an issue with passing template comparator.
template<typename T, typename C = less<T> > class Heap{
vector<T> *heap;
public:
Heap(vector<T> *arr){
heap = new vector<T> (arr->begin(), arr->end());
build_heap();
}
void build_heap(){
size_t n = heap->size();
for (size_t i=(n-1)/2; i>=0; i--){
shiftDown(i);
}
}
void shiftDown(size_t i){ /// heap logic
while(i < heap->size()){
size_t child = 2*i+1;
// int min_ind = 2*i+1;
if(child >= heap->size())
return;
if(child+1 < heap->size()){
if( C(heap->at(child+1),heap->at(child)) ){ // <----- using C as comparator
child++;
}
}
if( C(heap->at(child), heap->at(i)) ){ // <----- using C as comparator
swap(heap->at(child), heap->at(i));
i = child;
}
else
break;
}
}
};
int main(){
vector<int> v={8,7,6,5,4,3,2,1};
Heap<int, less<int> > heap(&v);
}
error
heap.cpp: In instantiation of ‘void Heap<T, C>::shiftDown(size_t) [with T = int; C = std::less<int>; size_t = long unsigned int]’:
heap.cpp:15:4: required from ‘void Heap<T, C>::build_heap() [with T = int; C = std::less<int>]’
heap.cpp:10:3: required from ‘Heap<T, C>::Heap(std::vector<_Tp>*) [with T = int; C = std::less<int>]’
heap.cpp:49:34: required from here
heap.cpp:32:9: error: no matching function for call to ‘std::less<int>::less(__gnu_cxx::__alloc_traits<std::allocator<int>, int>::value_type&, __gnu_cxx::__alloc_traits<std::allocator<int>, int>::value_type&)’
32 | if( C(heap->at(child+1),heap->at(child)) ){
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
detailed error
i'm following same syntex of declaration as stl c++ do, still i'm getting error. please help me out.
template<typename T, typename C = less<T> > class Heap;
any help or pointer to help is appreciated. thank you.
template<class T>
class Comparator{
bool operator()(const T &a, const T &b){
...
// returns logic
}
}
template<class T, class Comp >
class AnyClass{
public:
...
void function(){
// code ...
Comp<T>()(obj1, obj2);
}
...
}
calling sytex :
...
AnyClass *obj = new AnyClass<Type , Comparator>();
obj.function()
...
passing Comparator to templated class and when we need to compare objects
we create a functional object and call operator() with args to compare.
In question, that object is less<int>.
Comp<T>()(obj1, obj2);
Let's say I am trying to implement some math vector class.
As vector interface will be used in multiple places: array based vector, matrices return columns and rows as vector interface objects and etc.
I would like to overload +,- operators for my vectors. Each operator should return new constructed object of some vector implementation class.
But as you know operator overloading should return a value or a reference. I can not return a value, as I need runtime polymorphism, so I am left with references. But to have a reference that does not die after the function call object should be created in the heap.
So how should I manage the situation?
P.S. I could create a shared_ptr and return a reference to containing value, but it does not look like a good practice.
typedef unsigned int vector_idx_t;
template <class T, vector_idx_t size>
class vector {
public:
virtual ~vector();
virtual T& operator[](const vector_idx_t idx) = 0;
virtual vector<T, size>& operator+ (const T& a) const = 0;
virtual vector<T, size>& operator- (const T& a) const = 0;
virtual vector<T, size>& operator* (const T& a) const = 0;
virtual vector<T, size>& operator/ (const T& a) const = 0;
virtual vector<T, size>& operator+ (const vector<T, size>& vec2) const = 0;
virtual vector<T, size>& operator- (const vector<T, size>& vec2) const = 0;
};
template <class T, vector_idx_t size>
class array_vector: public vector<T, size> {
private:
std::array<T, size> m_elements;
public:
array_vector();
array_vector(std::array<T, size> elements);
array_vector(const vector<T, size>& vec2);
array_vector(std::initializer_list<T> elems);
virtual ~array_vector();
virtual T& operator[](const vector_idx_t idx) {
return m_elements[idx];
}
virtual vector<T, size>& operator+ (const T& a) const {
std::array<T, size> e;
for (vector_idx_t i = 0; i < size; ++i) {
e[i] = m_elements[i] + a;
}
auto v = std::make_shared<array_vector<T, size>>(elems);
return *v;
}
};
I suggest a slight modification to your design for accommodating the polymorphic nature of the implementation.
Don't make vector polymorphic.
Use a Data class to contain the implementation specific details of vector.
Make Data polymorphic.
That will allow you to return vectors by value or by reference, as appropriate to an interface.
Polymorphism by subtype is not the answer to all problems. I understand what are you trying to do but I don't exactly understand why a polymorphic by template solution is not enough and you need to have virtual operators (which don't mix well at all with polymorphism by subtype).
You want to be able to define operations on mixed types of vectors so that you can compute results between real containers and proxy to containers.
This first of all should require that you have a basic final type that you need, a proxy to a matrix column is not a real container but rather a view of a container, so adding two of them should return a real container (eg. a container backed by an actual std::array?).
A similar design could be managed by something like
template<typename ContainerType, typename ElementType>
class vector_of : public ContainerType
{
public:
vector_of(const ContainerType& container) : ContainerType(container) { }
vector_of<ContainerType, ElementType> operator+(const ElementType& a) const
{
vector_of<ContainerType, ElementType> copy = vector_of<ContainerType,ElementType>(*this);
std::for_each(copy.begin(), copy.end(), [&a](ElementType& element) { element += a; });
}
template<typename T>
vector_of<ContainerType, ElementType> operator+(const vector_of<T, ElementType>& a) const
{
vector_of<ContainerType, ElementType> copy(*this);
auto it = copy.begin();
auto it2 = a.begin();
while (it != copy.end() && it2 != a.end())
{
*it += *it2;
++it;
++it2;
}
return copy;
}
};
The trick here is that operator+ is a template method which accepts a generic container of ElementType elements. The code assumes that these kind of containers provide a begin and end methods which return an iterator (which is a smart choice in any case because it works well with STL).
With you can do things like:
class MatrixRowProxy
{
private:
int* data;
size_t length;
public:
MatrixRowProxy(int* data, size_t length) : data(data), length(length) { }
int* begin() const { return data; }
int* end() const { return data + length; }
};
vector_of<std::array<int, 5>, int> base = vector_of<std::array<int, 5>, int>({ 1, 2, 3, 4, 5 });
vector_of<std::vector<int>, int> element = vector_of<std::vector<int>, int>({ 2, 3, 4, 5, 6 });
int* data = new int[5] { 10, 20, 30, 40, 50};
vector_of<MatrixRowProxy, int> proxy = vector_of<MatrixRowProxy, int>(MatrixRowProxy(data, 5));
auto result = base + element + proxy;
for (const auto& t : result)
std::cout << t << std::endl;
So you can add heterogeneous kinds of vectors without the need of any virtual method.
Of course these methods require to create a new resulting object in the methods. This is done by copying this into a new vector_of<ContainerType, ElementType>. Nothing prevents you from adding a third template argument like VectorFactory which takes care of this so that you could use vectors which are only wrappers also on LHS of such operators.
Given the following template in a header file, and a couple of specializations:
template<typename> class A {
static const int value;
};
template<> const int A<int>::value = 1;
template<> const int A<long>::value = 2;
and building with clang-5, it results in errors for each source unit that included the file, all complaining about multiple definitions for A<int>::value and A<long>::value.
At first, I thought that maybe the template specializations needed to be put in a specific translation unit, but on checking the spec, this apparently should be allowed, because the value is a constant integer.
Am I doing something else wrong?
EDIT: if I move the definition into a single translation unit, then I can no longer use the value of A<T>::value in the context of a const int (eg, where its value is being used to calculate the value of another const assignment) , so the value really needs to be in a header.
In c++11 you maybe can go that way:
template<typename> class B {
public:
static const int value = 1;
};
template<> class B<long> {
public:
static const int value = 2;
};
template<typename T> const int B<T>::value;
If you only want to specialize the value var, you can use CRTP for that.
From C++17 you can make your definition inline:
template<> inline const int A<int>::value = 1;
template<> inline const int A<long>::value = 2;
Also from c++17 you can remove the 'template const int B::value;' for constexpr:
template<typename> class C {
public:
static constexpr int value = 1;
};
template<> class C<long> {
public:
static constexpr int value = 2;
};
// no need anymore for: template<typename T> const int C<T>::value;
And another solution for c++11 can be to use a inline method instead of inline vars which are allowed from c++17:
template<typename T> class D {
public:
static constexpr int GetVal() { return 0; }
static const int value = GetVal();
};
template <> inline constexpr int D<int>::GetVal() { return 1; }
template <> inline constexpr int D<long>::GetVal() { return 2; }
template< typename T>
const int D<T>::value;
In addition to your last edit:
To use your values also in other dependent definitions it seems to be the most readable version if you use the inline constexpr methods.
Edit: "Special" version for clang, because as OP tells us, clang complains with "specialization happening after instantiation". I don't know if clang or gcc is wrong in that place...
template<typename T> class D {
public:
static constexpr int GetVal();
static const int value;
};
template <> inline constexpr int D<int>::GetVal() { return 1; }
template <> inline constexpr int D<long>::GetVal() { return 2; }
template <typename T> const int D<T>::value = D<T>::GetVal();
int main()
{
std::cout << D<int>::value << std::endl;
std::cout << D<long>::value << std::endl;
}
I told already that CRTP is possible if not the complete class should be redefined. I checked the code on clang and it compiles without any warning or error, because OP comments that he did not understand how to use it:
template<typename> class E_Impl {
public:
static const int value = 1;
};
template<> class E_Impl<long> {
public:
static const int value = 2;
};
template<typename T> const int E_Impl<T>::value;
template < typename T>
class E : public E_Impl<T>
{
// rest of class definition goes here and must not specialized
// and the values can be used here!
public:
void Check()
{
std::cout << this->value << std::endl;
}
};
int main()
{
E<long>().Check();
std::cout << E<long>::value << std::endl;
E<int>().Check();
std::cout << E<int>::value << std::endl;
}
I have a number of C++ structs with a number of methods. The C++ structs have a
"default" instance, and I would like to expose a "c" wrapper functions that uses
this default instance. But I would also like to avoid repeating all the
prototyles.
Alkind of C++11/14/17 and/or macro tricks are welcome, but I do not want to use
code-generators.
I have something that almost works, but I'm still struggling with a few
details.
// C++ class that have a "default-instance" ///////////////////////////////////
struct Foo {
int a() { return 1; }
int b(int) { return 2; }
int c(int, int) { return 3; }
};
Foo *FOO = nullptr;
// emulating existing c code that can not be changed //////////////////////////
typedef int (*ptr_a_t)();
ptr_a_t ptr_a = nullptr;
typedef int (*ptr_b_t)(int);
ptr_b_t ptr_b = nullptr;
typedef int (*ptr_c_t)(int, int);
ptr_c_t ptr_c = nullptr;
// Wrapper code (almost generic) //////////////////////////////////////////////
template <typename T, T>
struct Proxy;
// Wrapper class that will use the defualt instance if initialized (FOO is
// hardcoded).
template <typename T, typename R, typename... Args, R (T::*mf)(Args...)>
struct Proxy<R (T::*)(Args...), mf> {
static R call(Args... args) {
if (FOO) {
// ^^^
return ((*FOO).*mf)(args...);
// HARD-CODED ^^^^
} else {
return -1;
}
}
};
// Helper function to deduce the Proxy-class (method 'b' is hardcoded)
template <typename T, typename R, typename... Args>
auto deduce_args(R (T::*mf)(Args...)) -> Proxy<R (T::*)(Args...), &T::b> {
// HARD-CODED ^
return Proxy<R (T::*)(Args...), &T::b>();
// HARD-CODED ^
}
// Wrap the methods ////////////////////////////////////////////////////////
//#define wrap_a decltype(deduce_args(&Foo::a))::call
#define wrap_b decltype(deduce_args(&Foo::b))::call
//#define wrap_c decltype(deduce_args(&Foo::c))::call
int main() {
// Test that it works
//ptr_a = &wrap_a; // does not work due to hard-coded method
ptr_b = &wrap_b;
//ptr_c = &wrap_c; // does not work due to hard-coded method
return ptr_b(0);
}
I can live with the hard-coded "FOO" in the proxy, as I only need one proxy per class, but it would be cool if the instance pointer could be passed as a
template argument.
The hard-coded method in "deduce_args" is really anoying, how can I eliminate
that??
Is there a better way to do this (the function pointers can not be replaced with std::function).
Using C++14 alias turned out to be a much easier way of achieving what I wanted.
// compile using the "-std=c++14" flag
// C++ class that have a "default-instance" ///////////////////////////////////
struct Foo {
int a() { return 1; }
int b(int) { return 2; }
int c(int, int) { return 3; }
};
Foo *FOO = nullptr;
// emulating existing c code that can not be changed //////////////////////////
typedef int (*ptr_a_t)();
ptr_a_t ptr_a = nullptr;
typedef int (*ptr_b_t)(int);
ptr_b_t ptr_b = nullptr;
typedef int (*ptr_c_t)(int, int);
ptr_c_t ptr_c = nullptr;
// Wrapper code ///////////////////////////////////////////////////////////////
template <typename T, T, typename P, P>
struct Proxy;
template <typename T, typename R, typename... Args, R (T::*mf)(Args...),
typename P, P p>
struct Proxy<R (T::*)(Args...), mf, P, p> {
static R call(Args... args) {
if (*p) {
return ((*(*p)).*mf)(args...);
} else {
return -1;
}
}
};
// Wrap the methods ///////////////////////////////////////////////////////////
#define WRAP(n, obj, m, ptr) \
const auto &n = Proxy<decltype(&obj::m), &obj::m, obj **, &ptr>::call
WRAP(wrap_a, Foo, a, FOO);
WRAP(wrap_b, Foo, b, FOO);
WRAP(wrap_c, Foo, c, FOO);
int main() {
// Test that it works
ptr_a = &wrap_a;
ptr_b = &wrap_b;
ptr_c = &wrap_c;
return ptr_b(0);
}
The common "solution" to use GetProcAddress with C++ is "extern "C", but that breaks overloading. Name mangling allows multiple functions to co-exist, as long as their signature differs. But is there a way to find these mangled names for GetProcAddress?
The VC++ compiler knows its own name mangling scheme, so why not use that? Inside template<typename T> T GetProcAddress(HMODULE h, const char* name), the macro __FUNCDNAME__ contains the mangled name of GetProcAddress. That includes the T part. So, inside GetProcAddress<void(*)(int), we have a substring with the mangled name of void(*)(int). From that, we can trivially derive the mangled name of void foo(int);
This code relies on the VC++ macro __FUNCDNAME__. For MinGW you'd need to base this on __PRETTY_FUNCTION__ instead.
FARPROC GetProcAddress_CppImpl(HMODULE h, const char* name, std::string const& Signature)
{
// The signature of T appears twice in the signature of T GetProcAddress<T>(HMODULE, const char*)
size_t len = Signature.find("##YA");
std::string templateParam = Signature.substr(0, len);
std::string returnType = Signature.substr(len+4);
returnType.resize(templateParam.size()); // Strip off our own arguments (HMODULE and const char*)
assert(templateParam == returnType);
// templateParam and returnType are _pointers_ to functions (P6), so adjust to function type (Y)
std::string funName = "?" + std::string(name) + "##Y" + templateParam.substr(2);
return ::GetProcAddress(h, funName.c_str());
}
template <typename T>
T GetProcAddress(HMODULE h, const char* name)
{
// Get our own signature. We use `const char* name` to keep it simple.
std::string Signature = __FUNCDNAME__ + 18; // Ignore prefix "??$GetProcAddress#"
return reinterpret_cast<T>(GetProcAddress_CppImpl(h, name, Signature));
}
// Showing the result
struct Dummy { };
__declspec(dllexport) void foo( const char* s)
{
std::cout << s;
}
__declspec(dllexport) void foo( int i, Dummy )
{
std::cout << "Overloaded foo(), got " << i << std::endl;
}
__declspec(dllexport) void foo( std::string const& s )
{
std::cout << "Overloaded foo(), got " << s << std::endl;
}
__declspec(dllexport) int foo( std::map<std::string, double> volatile& )
{
std::cout << "Overloaded foo(), complex type\n";
return 42;
}
int main()
{
HMODULE h = GetModuleHandleW(0);
foo("Hello, ");
auto pFoo1 = GetProcAddress<void (*)( const char*)>(h, "foo");
// This templated version of GetProcAddress is typesafe: You can't pass
// a float to pFoo1. That is a compile-time error.
pFoo1(" world\n");
auto pFoo2 = GetProcAddress<void (*)( int, Dummy )>(h, "foo");
pFoo2(42, Dummy()); // Again, typesafe.
auto pFoo3 = GetProcAddress<void (*)( std::string const& )>(h, "foo");
pFoo3("std::string overload\n");
auto pFoo4 = GetProcAddress<int (*)( std::map<std::string, double> volatile& )>(h, "foo");
// pFoo4 != NULL, this overload exists.
auto pFoo5 = GetProcAddress<void (*)( float )>(h, "foo");
// pFoo5==NULL - no such overload.
}
Use dumpbin /exports 'file.dll' to get the decorated / undecorated name of all the symbols.
It's impossible to do it just by using GetProcAddress. However, one way to do it would be to enumerate all the exported functions for that particular module, and do a pattern matching to find all the mangled names.
More specifically, refer to this answer here. The only change you will need to make would be to pass in TRUE for MappedAsImage parameter and the return value of GetModuleHandle for Base parameter to ImageDirectoryEntryToData function call.
void EnumerateExportedFunctions(HMODULE hModule, vector<string>& slListOfDllFunctions)
{
DWORD *dNameRVAs(0);
_IMAGE_EXPORT_DIRECTORY *ImageExportDirectory;
unsigned long cDirSize;
_LOADED_IMAGE LoadedImage;
string sName;
slListOfDllFunctions.clear();
ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)
ImageDirectoryEntryToData(hModule,
TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);
if (ImageExportDirectory != NULL)
{
dNameRVAs = (DWORD *)ImageRvaToVa(LoadedImage.FileHeader,
LoadedImage.MappedAddress,
ImageExportDirectory->AddressOfNames, NULL);
for(size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
{
sName = (char *)ImageRvaToVa(LoadedImage.FileHeader,
LoadedImage.MappedAddress,
dNameRVAs[i], NULL);
slListOfDllFunctions.push_back(sName);
}
}
}
I can't quite fathom why you'd ever want/need a constexpr version of MSalters' solution, but here it is, complete with namespace mangling. Use as
using F = int(double*);
constexpr auto f = mangled::name<F>([]{ return "foo::bar::frobnicate"; });
constexpr const char* cstr = f.data();
where F is the function signature and foo::bar::frobnicate is the (possibly qualified) name of the function.
#include<string_view>
#include<array>
namespace mangled
{
namespace detail
{
template<typename F>
inline constexpr std::string_view suffix()
{
auto str = std::string_view(__FUNCDNAME__);
return str.substr(14, str.size() - 87);
}
template<typename L>
struct constexpr_string
{
constexpr constexpr_string(L) {}
static constexpr std::string_view data = L{}();
};
template<typename Name>
inline constexpr int qualifiers()
{
int i = -2, count = -1;
while(i != std::string_view::npos)
{
i = Name::data.find("::", i + 2);
count++;
}
return count;
}
template<typename Name>
inline constexpr auto split()
{
std::array<std::string_view, qualifiers<Name>() + 1> arr = {};
int prev = -2;
for(int i = arr.size() - 1; i > 0; i--)
{
int cur = Name::data.find("::", prev + 2);
arr[i] = Name::data.substr(prev + 2, cur - prev - 2);
prev = cur;
}
arr[0] = Name::data.substr(prev + 2);
return arr;
}
template<typename F, typename Name>
struct name_builder
{
static constexpr auto suf = detail::suffix<F>();
static constexpr auto toks = split<Name>();
static constexpr auto len = Name::data.size() + suf.size() - toks.size() + 6;
static constexpr auto str = [] {
std::array<char, len> arr = {};
arr[0] = '?';
int i = 1;
for(int t = 0; t < toks.size(); t++)
{
if(t > 0)
arr[i++] = '#';
for(auto c : toks[t])
arr[i++] = c;
}
arr[i++] = '#';
arr[i++] = '#';
arr[i++] = 'Y';
for(auto c : suf)
arr[i++] = c;
return arr;
}();
};
}
template<typename F, typename LambdaString>
inline constexpr std::string_view name(LambdaString)
{
using Cs = detail::constexpr_string<LambdaString>;
using N = detail::name_builder<F, Cs>;
return {N::str.data(), N::len};
}
}
GodBolt