Я пытаюсь обернуть вариант стиля c в библиотеку. Он использует тип enum
, который я имею в виду для заполнения с помощью шаблонов.
Упрощенная версия выглядит примерно так:
enum Type
{
Type_Int,
Type_Double,
Type_String
};
template<typename T>
Type typeId();
template<> Type typeId<int>() { return Type_Int; }
template<> Type typeId<double>(){ return Type_Double; }
template<> Type typeId<const char*>() { return Type_String; }
template<> Type typeId<char[10]>() { return Type_String; }
// not allowed
// template<int N> Type typeId<char[N]>() { return Type_String; }
https://godbolt.org/z/3GsKv6PEn
Я прекрасно распознаю char[10]
, но хочу сделать общий случай char[N]
, что недопустимо. Так как же мне распознать все типы char[N]
?
пс. Он встроен, поэтому я предпочитаю не использовать std::string
там, где это возможно.
Возможно, вы могли бы показать немного больше API «C», которым вам приходится довольствоваться. На самом деле, возможно, было бы проще использовать std::variant на стороне C++ и написать функцию преобразования.
Пожалуйста, также включите определение struct
/union
, которое используется на стороне C.
С помощью c++20 вы можете объединить концепцию и std::is_array
как это сделано здесь.
На самом деле это не вариант библиотеки, но они используют перечисление и стирание типов. Это вот этот для тех, кому интересно, но это будет интересно прочитать всем, кто отвечает на простой ТАК вопрос :)
Как насчет:
template<int N> Type typeId(char(& a)[N]) { return Type_String; }
Это не совсем тот же синтаксис, но он будет работать так же, как и предыдущий, поскольку вы можете указать тип с помощью параметра шаблона.
См. пример
И кстати, для этого маппинга вам вообще не нужен шаблон. Решение с базовой перегрузкой функций будет работать:
#include <iostream>
enum Type{
Type_Int,
Type_Double,
Type_String,
};
constexpr Type typeId(int) { return Type_Int; }
constexpr Type typeId(double){ return Type_Double; }
constexpr Type typeId(const char*) { return Type_String; }
int main(int argc, char** argv)
{
int e;
std::cout << typeId(e) << std::endl;
double d;
std::cout << typeId(d) << std::endl;
const char * c;
std::cout << typeId(c) << std::endl;
char b[10];
std::cout << typeId(b) << std::endl;
char a[11];
std::cout << typeId(a) << std::endl;
}
Функция может быть constexpr, поэтому вы также можете извлечь Type
во время компиляции.
Наконец, вот версия вообще без функции, поскольку она не является строго обязательной. Это имеет самые низкие накладные расходы (поймите: нет)
#include <iostream>
enum Type{
Type_Int,
Type_Double,
Type_String,
};
template <typename T> constexpr Type Map;
template <> constexpr Type Map<int> = Type_Int;
template <> constexpr Type Map<double> = Type_Double;
template <> constexpr Type Map<const char*> = Type_String;
template <size_t N> constexpr Type Map<char[N]> = Type_String;
int main(int argc, char** argv)
{
std::cout << Map<int> << std::endl;
std::cout << Map<double> << std::endl;
std::cout << Map<const char*> << std::endl;
std::cout << Map<char[10]> << std::endl;
std::cout << Map<char[11]> << std::endl;
}
Полный пример с инверсией карты (Type
к фактическому типу C++) здесь
Кроме того, для этого вам не нужен C++20 (в любом случае он может быть недоступен в вашем встроенном наборе инструментов разработки).
В вопросе typeId
функция не имеет аргументов. Фактически, с этим решением вам вообще не нужны шаблоны: godbolt.org/z/P6Wr5Y38z разрешение перегрузки возьмет на себя.
Вы можете обернуть тип std::type_identity
, чтобы избежать фиктивной переменной: constexpr Type typeId(std::type_identity<int>) { return Type_Int; }
Есть так много способов сделать одно и то же. Я перечислил 3 из них. Последний, пожалуй, самый чистый.
@Jarod42: Да, правда. Дело в том, что вам не нужна специализация шаблонов, чтобы вывести тип C-распавшегося, достаточно только перегрузки функций.
Вы не можете частично специализировать функцию. Но вы можете частично специализировать класс или структуру.
Итак, чтобы решить эту проблему, выполните логику шаблона в классе, а затем используйте ее в функции:
enum Type {
Type_Int,
Type_Double,
Type_String,
};
namespace detail {
template <typename T>
class TypeId;
template <>
class TypeId<int> {
public:
static constexpr Type value = Type_Int;
};
template <>
class TypeId<double> {
public:
static constexpr Type value = Type_Double;
};
template <>
class TypeId<const char*> {
public:
static constexpr Type value = Type_String;
};
template <int N>
class TypeId<const char[N]> {
public:
static constexpr Type value = Type_String;
};
}
template <typename T>
constexpr Type typeId()
{
return detail::TypeId<T>::value;
}
static_assert(typeId<int>() == Type_Int);
static_assert(typeId<double>() == Type_Double);
static_assert(typeId<const char*>() == Type_String);
static_assert(typeId<const char[2]>() == Type_String);
static_assert(typeId<const char[10]>() == Type_String);
https://godbolt.org/z/4zfb47Mvx
Чтобы сделать это более устойчивым к вариациям типов cv-квалификаторов, вы можете использовать std::remove_cvref_t : https://godbolt.org/z/esGf4Wc8h
С c++20 вы можете использовать концепцию и написать черту/проверку, например std::is_array, чтобы проверить, является ли тип массивом символов, как показано ниже.
template<class T>
struct is_char_array : std::false_type {};
template<>
struct is_char_array<char[]> : std::true_type {};
template<std::size_t N>
struct is_char_array<char[N]> : std::true_type {};
template< class T >
inline constexpr bool is_char_array_v = is_char_array<T>::value;
template<typename T> concept checkArray = is_char_array_v<T>;
template<checkArray T> Type typeId()
{
std::cout << "char N version\n";
return Type_String;
}
Как мне распознать все типы
char[N]
?
Специализация классов должна быть предпочтительным способом, который работает всегда.
Однако в вашем случае вы можете использовать if constexpr следующим образом (требуется c++17 или более поздняя версия), чтобы создать одну функцию typeId()
для всех Type
случаев:
template<typename T>
inline constexpr Type typeId() /* noexcept */
{
if constexpr (std::is_same_v<T, int>) return Type_Int;
else if constexpr (std::is_same_v<T, double>) return Type_Double;
else if constexpr (std::is_same_v<T, const char*>
|| (std::is_array_v<T>
&& std::is_same_v<std::remove_const_t<std::remove_extent_t<T>>, char>)
)
return Type_String;
}
Если существуют многомерные массивы char
, вы можете заменить std::remove_extent_t на std::remove_all_extents в приведенном выше примере.
Чтобы иметь частичную специализацию, вам нужен класс. Итак, создайте логику шаблона в классе, а затем используйте этот класс в этой функции.