[英]Is there a way to create a compile time type map in C++17 for type-checking?
我對 C++ 元編程/SFINAE 有點陌生,並且在開發檢查以查看傳入方法的類型是否包含在預定義的類型列表中時遇到了麻煩。 這里的上下文是我想檢查在我的變體中注冊的類型是否與另一個結構中的 output 類型匹配。 在我的應用程序中注冊的每個項目都通過標簽(某個數字)映射到另一個項目(在結構中)。 如果要注冊的類型與我的有線協議結構中的項目類型不匹配,我想創建一個可以在編譯時使用的類型映射來引發斷言。
所以像:
// register elements in type map:
type_map::register(ID_1, decltype(wire_type_item_1));
type_map::register(ID_2, decltype(wire_type_item_2));
... etc.
// and when types are registered
template<typename T>
void add_item(const uint32_t id, const T item)
{
// add static_assert here
// look up type based on ID, and compare to type passed in
// when add_item is called
static_assert(std::is_same<type_map::find(id), decltype(T),
"Attempted to register type that does not match wire type");
...
}
我會很感激任何關於從哪里開始/如何到 go 關於這樣做的指針 - 謝謝!
遵循(和誤解?)Yakk(謝謝)在我的第一個回答中的評論中的建議,我編寫了一種非常不同的(更簡單,更優雅,恕我直言)方式來實現編譯時間 map。
首先, ct_pair
。 現在更復雜的是using
類型type
(獲取模板類型T
參數)和static
function ,給定結構的相同索引(包裝在std::integral_constant
中,具有來自ct_pair
不同實現的不同類型)
template <std::size_t I, typename T>
struct ct_pair
{
using type = T;
static ct_pair get_pair (std::integral_constant<std::size_t, I>)
{ return {}; }
};
現在std::tuple
, std::tuple_cat()
和助手 class get_tuple
不再需要,而ct_map
簡單地變成
template <typename ... Ps>
struct ct_map : public Ps...
{
using Ps::get_pair...;
template <std::size_t I>
using find_type
= typename decltype(get_pair(
std::integral_constant<std::size_t, I>{}))::type;
};
現在完整的例子(不幸的是 C++17 而不是之前,當我的另一個答案應該是 C++11/C++14 兼容時)變成
#include <tuple>
#include <iostream>
#include <type_traits>
template <std::size_t I, typename T>
struct ct_pair
{
using type = T;
static ct_pair get_pair (std::integral_constant<std::size_t, I>)
{ return {}; }
};
template <typename ... Ps>
struct ct_map : public Ps...
{
using Ps::get_pair...;
template <std::size_t I>
using find_type
= typename decltype(get_pair(
std::integral_constant<std::size_t, I>{}))::type;
};
using type_map = ct_map<ct_pair<2u, char>,
ct_pair<3u, int>,
ct_pair<5u, long>,
ct_pair<7u, long long>>;
int main()
{
static_assert( std::is_same_v<type_map::find_type<5u>, long> );
}
這是在類之間創建編譯時 map 的一種方法。
#include <type_traits>
struct foo {};
struct bar {};
struct ID_1 {};
struct ID_2 {};
struct ID_3 {};
template <class T>
struct type_map {};
template <>
struct type_map<ID_1> { using type = foo; }; // map ID_1 -> foo
template <>
struct type_map<ID_2> { using type = bar; }; // map ID_2 -> bar
int main() {
static_assert(std::is_same_v<type_map<ID_1>::type, foo>);
static_assert(std::is_same_v<type_map<ID_2>::type, bar>);
static_assert(std::is_same_v<type_map<ID_3>::type, int>); // fails, ID_3 isn't in map
static_assert(std::is_same_v<type_map<ID_1>::type, bar>); // fails, ID_1 is mapped to foo, not bar
}
但是,在您的代碼示例中,您有以下行: static_assert(std::is_same<type_map::find(id), decltype(T), "Attempted to register type that does not match wire type");
問題在於id
是一個運行時變量。 您不能在運行時使用static_assert
。
如果您確實想在運行時在此 map 中查找類型,則要復雜一些。 我建議使用像hana
這樣的元編程庫,因為它允許您在運行時循環遍歷編譯時數據結構的所有元素。
不知道你到底想要什么......無論如何......
當然,您不能使用const std::uint32_t id
值來獲取( find(id)
)可以在static_assert()
template <typename T>
void add_item (std::uint32_t const id, T const item)
// ................................^^ the value id is unusable in a static_assert()
如果您知道id
編譯時間的值(否則您的問題沒有意義),您可以將它(我建議作為std::size_t
)作為模板值傳遞給std::integral_constant
template <std::size_t ID, uintypename T>
void add_item (std::integral_constant<std::size_t, ID>, T const item)
或者更好,我想,直接作為模板參數,你必須顯式調用add_item()
。
無論如何...對於 map 類型,我首先建議在std::size_t
和類型之間建立一個ct_pair
(編譯時間對)
template <std::size_t, typename>
struct ct_pair
{ };
還給出了幾個輔助結構如下
template <std::size_t, std::size_t, typename>
struct get_tuple
{ using type = std::tuple<>; };
template <std::size_t I, typename T>
struct get_tuple<I, I, T>
{ using type = std::tuple<T>; };
您可以使用模板專業化、 std::tuple_cat()
的強大功能以及std::get_val()
和decltype()
創建一個ct_map
(編譯時間圖),如下所示
template <typename ...>
struct ct_map;
template <std::size_t ... Is, typename ... Ts>
struct ct_map<ct_pair<Is, Ts>...>
{
template <std::size_t I>
static constexpr auto find_type_func ()
-> decltype( std::get<0>( std::tuple_cat(
std::declval<typename get_tuple<I, Is, Ts>::type>()...)) );
template <std::size_t I>
using find_type
= std::remove_reference_t<decltype( find_type_func<I>() )>;
};
要在 map 上注冊元素,您必須定義一個using
using type_map = ct_map<ct_pair<2u, char>,
ct_pair<3u, int>,
ct_pair<5u, long>,
ct_pair<7u, long long>>;
並且static_assert()
檢查變成如下
static_assert( std::is_same_v<type_map::find_type<5u>, long> );
下面是一個完整的編譯C++17的例子
#include <tuple>
#include <iostream>
#include <type_traits>
template <std::size_t, typename>
struct ct_pair
{ };
template <std::size_t, std::size_t, typename>
struct get_tuple
{ using type = std::tuple<>; };
template <std::size_t I, typename T>
struct get_tuple<I, I, T>
{ using type = std::tuple<T>; };
template <typename ...>
struct ct_map;
template <std::size_t ... Is, typename ... Ts>
struct ct_map<ct_pair<Is, Ts>...>
{
template <std::size_t I>
static constexpr auto find_type_func ()
-> decltype( std::get<0>( std::tuple_cat(
std::declval<typename get_tuple<I, Is, Ts>::type>()...)) );
template <std::size_t I>
using find_type
= std::remove_reference_t<decltype( find_type_func<I>() )>;
};
using type_map = ct_map<ct_pair<2u, char>,
ct_pair<3u, int>,
ct_pair<5u, long>,
ct_pair<7u, long long>>;
int main()
{
static_assert( std::is_same_v<type_map::find_type<5u>, long> );
}
template<auto i>
using constant = std::integral_constant<decltype(i), i>;
template<class T>
struct tag_t {
using type=T;
// comparison with other tags!
constexpr auto operator==( tag_t<T> ) const { return std::true_type{}; }
constexpr auto operator!=( tag_t<T> ) const { return std::false_type{}; }
template<class U>
constexpr auto operator==( tag_t<U> ) const { return std::false_type{}; }
template<class U>
constexpr auto operator!=( tag_t<U> ) const { return std::true_type{}; }
};
template<class T>
constexpr tag_t<T> tag = {};
現在我們可以使用整數和類型作為值。
type_map::register(ID_1, decltype(wire_type_item_1));
type_map::register(ID_2, decltype(wire_type_item_2));
... etc.
這變成
auto wire_type_map( constant<ID_1> ) { return tag<wire_type_item_1>; }
auto wire_type_map( constant<ID_2> ) { return tag<wire_type_item_1>; }
只要在使用點可見(從 header 文件中),此注冊可以以分布式方式完成。
// and when types are registered
template<typename T, uint32_t id>
void add_item(constant<id> id_tag, const T item)
{
static_assert(wire_type_map( id_tag ) == tag<T>,
"Attempted to register type that does not match wire type");
...
}
這確實要求添加項目的 integer 值此時是編譯時間常數。
通常,如果您正在閱讀 integer 和離線數據,這是不切實際的; 無法在編譯時檢查您正在閱讀的 integer 是否與類型匹配。 但是可以在編譯時檢查您提供的 integer 是否與類型匹配。
OTOH,將 integer 參數完全剝離,並將 map 類型改為 integer 可能更容易。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.