[英]How to link two unrelated classes in C++ free function using template metaprogramming
我有一個序列化流操作符作為這樣的免費功能:
struct MyClass {
static size_t size() { return 24; } // whatever my expected size is
X x; Y y;
};
Archive& operator<<(Archive& ar, MyClass& c) {
ar << c.x;
ar << c.y;
return ar;
}
我有很多這樣的類和免費的函數運算符。
我想添加一個static_assert
編譯時檢查,它將自動觸發以對 MyClass 進行大小檢查(對於某些開發人員已將字段添加到 MyClass 並忘記對其進行序列化的情況)。 它將調用MyClass::size()
以獲取預期大小並與sizeof(MyClass)
進行比較。
我不想更改所有operator<<
定義來執行此操作。 這很乏味,容易出錯,並且不會實現最初的意圖:自動運行檢查而無需開發人員明確編寫檢查(因為這永遠不會發生)。 另外,序列化代碼來自庫,所以我不想更改原始代碼。
我在想——通過元編程——我可以讓存檔知道我正在序列化 MyClass。 然后它可以做這樣的檢查:
static_assert(sizeof(MyClass) == MyClass::size();
但是如何做到這一點呢? 如果我讓 Archive 期望一個值為 MyClass 的模板參數,那么 operator<< 中的每一行都必須更改,因為每個 ar 將是不同類的實例:
Archive<MyClass>& operator<<(Archive<MyClass>& ar, MyClass& c) {
Archive<X> arX; arX << c.x;
Archive<Y> arY; arY << c.y;
return ar;
}
有什么絕妙的想法嗎? 謝謝!
對我來說,這僅在常量來自重載時才有效,並且您無法在不更新重載而不是某些類方法的情況下消除錯誤。
但是正如您所提到的,您不能向<<
運算符添加參數。 無論如何,讓我們嘗試一下,因為您暗示可以通過將簽名轉換為函數來更新簽名:
template <size_t N>
using ExpectedSize = std::integral_constant<size_t, N>;
Archive& Serialize(Archive& ar, MyClass& c, ExpectedSize<24>) {
ar << c.x;
ar << c.y;
return ar;
}
然后,從一個包羅萬象的重載中調用它:
template <typename T>
Archive& operator <<(Archive ar, T&& c) {
return Serialize(ar, c, ExpectedSize<sizeof(typename std::remove_reference<T>::type)>{});
}
// non-template overloads for basic primitives
Archive& operator <<(Archive& ar, int c) { return ar; }
Archive& operator <<(Archive& ar, const char* c) { return ar; }
現在,當您運行代碼並且sizeof(MyClass) == 16
而不是 24 時,您會收到此錯誤:
error: no matching function for call to 'Serialize(Archive&, MyClass&, ExpectedSize<16>)'
...
note: candidate: 'Archive& Serialize(Archive&, MyClass&, ExpectedSize<24>)'
演示: https : //godbolt.org/z/sBFtBR
如果你真的想要更具體的錯誤信息,你可以添加一個模板來捕捉丟失的重載:
template <typename T, size_t N>
Archive& Serialize(Archive& ar, T&& c, ExpectedSize<N>) {
static_assert(sizeof(typename std::remove_reference<T>::type) != N, "Serializer needs to be updated");
}
然后你的錯誤信息變成:
<source>: In instantiation of 'Archive& Serialize(Archive&, T&&, ExpectedSize<N>) [with T = MyClass&; long unsigned int N = 16; ExpectedSize<N> = std::integral_constant<long unsigned int, 16>]':
<source>:11:21: required from 'Archive& operator<<(Archive, T&&) [with T = MyClass&]'
<source>:40:12: required from here
<source>:34:67: error: static assertion failed: Serializer needs to be updated
演示: https : //godbolt.org/z/wQAvGg
由於您的最終目標是強制序列化函數和類定義之間的一致性,您不妨考慮自動生成序列化方法。 這將比接受的答案更復雜,但從長遠來看可能會為您節省一些時間。 我知道兩種方法都需要一些討厭的技巧:一種依賴於這里描述的包裝類http://cplusplus.bordoon.com/dark_side.html ,另一種是使用 xmacro 技術https://en.wikipedia.org /wiki/X_Macro最終能夠做這樣的事情 -> https://github.com/asherikov/ariles#example 。
這是我想出的。 基本上我使用@parktomatomi 的用戶編寫的Serialize
例程的想法,但現在它被一個包羅萬象的template<class T> operator<<(Archive&, T& c)
調用,它也執行static_assert
以進行大小檢查。
struct B {
constexpr static size_t size() { return 20; }
int y = 200;
};
struct C {
constexpr static size_t size() { return 10; }
int x = 100;
B b;
};
template<typename T>
Archive& Serialize(Archive& ar, T& c) {
abort(); // Should never get here
}
Archive& operator <<(Archive& ar, int x) {
std::cout << "ar << " << x << std::endl;
return ar;
}
template <typename T>
Archive& operator <<(Archive& ar, T& c) {
static_assert(sizeof(T) == T::size());
return Serialize<T>(ar, c);
}
template<>
Archive& Serialize(Archive& ar, B& b) {
std::cout << "ar<B> << " << b.y << std::endl;
ar << b.y;
return ar;
}
template<>
Archive& Serialize(Archive& ar, C& c) {
std::cout << "ar<B> << " << c.x << std::endl;
ar << c.b;
ar << c.x;
return ar;
};
int main(int argc, char* argv[])
{
Archive ar;
C c;
ar << c;
//std::cout << foo(2);
}
它產生
a.cpp: In instantiation of âArchive& operator<<(Archive&, T&) [with T = B]â:
a.cpp:91:11: required from here
a.cpp:77:27: error: static assertion failed
static_assert(sizeof(T) == T::size());
~~~~~~~~~~^~~~~~~~~~~~
a.cpp: In instantiation of âArchive& operator<<(Archive&, T&) [with T = C]â:
a.cpp:100:9: required from here
a.cpp:77:27: error: static assertion failed
現在我需要想出一個更好的信息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.