[英]Is it possible to concatenate two strings of type `const char *` at compile time?
Obviously we can concatenate two string literals in a constexpr
function, but what about concatenation of a string literal with a string returned by another constexpr
function as in the code below?显然,我们可以在constexpr
函数中连接两个字符串文字,但是如何将字符串文字与另一个constexpr
函数返回的字符串连接起来,如下面的代码所示?
template <class T>
constexpr const char * get_arithmetic_size()
{
switch (sizeof(T))
{
case 1: return "1";
case 2: return "2";
case 4: return "4";
case 8: return "8";
case 16: return "16";
default: static_assert(dependent_false_v<T>);
}
}
template <class T>
constexpr std::enable_if_t<std::is_arithmetic_v<T>, const char *> make_type_name()
{
const char * prefix = std::is_signed_v<T> ? "int" : "uint";
return prefix; // how to concatenate prefix with get_arithmetic_size<T>() ?
}
static_assert(strings_equal(make_type_name<int>, make_type_name<int32_t>);
The code makes compiler-independent string identifier of an arithmetic type.该代码使算术类型的与编译器无关的字符串标识符。
EDIT1:编辑1:
A little bit more complicated example is:稍微复杂一点的例子是:
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
template <class T>
constexpr std::enable_if_t<is_specialization<T, std::vector>::value || is_specialization<T, std::list>::value, const char *> make_type_name()
{
return "sequence"; // + make_type_name<typename T::value_type>;
}
static_assert(strings_equal(make_type_name<std::vector<int>>(), make_type_name<std::list<int>>()));
No, it's impossible.不,这是不可能的。 You can implement something like below (it's C++14).您可以实现如下所示的内容(它是 C++14)。
#include <cmath>
#include <cstring>
#include <iostream>
#include <type_traits>
constexpr const char* name[] = {
"uint1", "uint2", "uint4", "uint8", "uint16",
"int1", "int2", "int4", "int8", "int16"
};
template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
return name[std::is_signed<T>::value * 5 +
static_cast<int>(std::log(sizeof(T)) / std::log(2) + 0.5)];
}
static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);
int main() {
std::cout << make_type_name<int>();
return 0;
}
https://ideone.com/BaADaM https://ideone.com/BaADAM
If you don't like using <cmath>
, you may replace std::log
:如果您不喜欢使用<cmath>
,您可以替换std::log
:
#include <cstring>
#include <iostream>
#include <type_traits>
constexpr const char* name[] = {
"uint1", "uint2", "uint4", "uint8", "uint16",
"int1", "int2", "int4", "int8", "int16"
};
constexpr size_t log2(size_t n) {
return (n<2) ? 0 : 1 + log2(n/2);
}
template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
return name[std::is_signed<T>::value * 5 + log2(sizeof(T))];
}
static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);
int main() {
std::cout << make_type_name<int>();
return 0;
}
Here is a quick compile time string class:这是一个快速编译时字符串类:
template<std::size_t N>
struct ct_str
{
char state[N+1] = {0};
constexpr ct_str( char const(&arr)[N+1] )
{
for (std::size_t i = 0; i < N; ++i)
state[i] = arr[i];
}
constexpr char operator[](std::size_t i) const { return state[i]; }
constexpr char& operator[](std::size_t i) { return state[i]; }
constexpr explicit operator char const*() const { return state; }
constexpr char const* data() const { return state; }
constexpr std::size_t size() const { return N; }
constexpr char const* begin() const { return state; }
constexpr char const* end() const { return begin()+size(); }
constexpr ct_str() = default;
constexpr ct_str( ct_str const& ) = default;
constexpr ct_str& operator=( ct_str const& ) = default;
template<std::size_t M>
friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
{
ct_str<N+M> retval;
for (std::size_t i = 0; i < N; ++i)
retval[i] = lhs[i];
for (std::size_t i = 0; i < M; ++i)
retval[N+i] = rhs[i];
return retval;
}
friend constexpr bool operator==( ct_str lhs, ct_str rhs )
{
for (std::size_t i = 0; i < N; ++i)
if (lhs[i] != rhs[i]) return false;
return true;
}
friend constexpr bool operator!=( ct_str lhs, ct_str rhs )
{
for (std::size_t i = 0; i < N; ++i)
if (lhs[i] != rhs[i]) return true;
return false;
}
template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs ) { return true; }
template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
friend bool operator==( ct_str, ct_str<M> ) { return false; }
};
template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;
you can use it like this:你可以这样使用它:
template <class T>
constexpr auto get_arithmetic_size()
{
if constexpr (sizeof(T)==1)
return ct_str{"1"};
if constexpr (sizeof(T)==2)
return ct_str{"2"};
if constexpr (sizeof(T)==4)
return ct_str{"4"};
if constexpr (sizeof(T)==8)
return ct_str{"8"};
if constexpr (sizeof(T)==16)
return ct_str{"16"};
}
template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
if constexpr (std::is_signed<T>{})
return ct_str{"int"} + get_arithmetic_size<T>();
else
return ct_str{"uint"} + get_arithmetic_size<T>();
}
which leads to statements like:这会导致以下语句:
static_assert(make_type_name<int>() == make_type_name<int32_t>());
passing.通过。
Now one annoying thing is that the length of the buffer is in the type system.现在一件烦人的事情是缓冲区的长度在类型系统中。 You could add a length
field, and make N
be "buffer size", and modify ct_str
to only copy up to length
and leave the trailing bytes as 0
.您可以添加一个length
字段,并使N
为“缓冲区大小”,并将ct_str
修改为仅复制到length
并将尾随字节保留为0
。 Then override common_type
to return the max N
of both sides.然后覆盖common_type
以返回双方的最大N
That would permit you do pass ct_str{"uint"}
and ct_str{"int"}
in the same type of value and make the implementation code a bit less annoying.这将允许您在相同类型的值中传递ct_str{"uint"}
和ct_str{"int"}
并使实现代码不那么烦人。
template<std::size_t N>
struct ct_str
{
char state[N+1] = {0};
template<std::size_t M, std::enable_if_t< (M<=N+1), bool > = true>
constexpr ct_str( char const(&arr)[M] ):
ct_str( arr, std::make_index_sequence<M>{} )
{}
template<std::size_t M, std::enable_if_t< (M<N), bool > = true >
constexpr ct_str( ct_str<M> const& o ):
ct_str( o, std::make_index_sequence<M>{} )
{}
private:
template<std::size_t M, std::size_t...Is>
constexpr ct_str( char const(&arr)[M], std::index_sequence<Is...> ):
state{ arr[Is]... }
{}
template<std::size_t M, std::size_t...Is>
constexpr ct_str( ct_str<M> const& o, std::index_sequence<Is...> ):
state{ o[Is]... }
{}
public:
constexpr char operator[](std::size_t i) const { return state[i]; }
constexpr char& operator[](std::size_t i) { return state[i]; }
constexpr explicit operator char const*() const { return state; }
constexpr char const* data() const { return state; }
constexpr std::size_t size() const {
std::size_t retval = 0;
while(state[retval]) {
++retval;
}
return retval;
}
constexpr char const* begin() const { return state; }
constexpr char const* end() const { return begin()+size(); }
constexpr ct_str() = default;
constexpr ct_str( ct_str const& ) = default;
constexpr ct_str& operator=( ct_str const& ) = default;
template<std::size_t M>
friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
{
ct_str<N+M> retval;
for (std::size_t i = 0; i < lhs.size(); ++i)
retval[i] = lhs[i];
for (std::size_t i = 0; i < rhs.size(); ++i)
retval[lhs.size()+i] = rhs[i];
return retval;
}
template<std::size_t M>
friend constexpr bool operator==( ct_str lhs, ct_str<M> rhs )
{
if (lhs.size() != rhs.size()) return false;
for (std::size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i]) return false;
return true;
}
template<std::size_t M>
friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs )
{
if (lhs.size() != rhs.size()) return true;
for (std::size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i]) return true;
return false;
}
};
template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;
The function implementations now become:函数实现现在变成:
template <class T>
constexpr ct_str<2> get_arithmetic_size()
{
switch (sizeof(T)) {
case 1: return "1";
case 2: return "2";
case 4: return "4";
case 8: return "8";
case 16: return "16";
}
}
template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
constexpr auto base = std::is_signed<T>{}?ct_str{"int"}:ct_str{"uint"};
return base + get_arithmetic_size<T>();
}
which is a lot more natural to write.这是很多写的更自然。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.