簡體   English   中英

如何使用模板元編程在 C++ 自由函數中鏈接兩個不相關的類

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM