簡體   English   中英

具有元組的C ++可變參數模板

[英]C++ variadic templates with tuples

我想編寫一個函數從二進制緩沖區中提取一些數據(假設數據是按順序存儲的)。 該函數在提取數據后返回數據和指針,如下所示

std::tuple<const unsigned char *, int, double, float> data = Extract<int, double, float>(p_buffer);

它從p_buffer提取一個int ,一個double和一個float數,並且data的第一個值指示從哪里開始下一個提取工作。

我試圖寫這樣的東西。

#include <tuple>

typedef unsigned char byte;

template<class TFirst, class... TRest>
struct Extractor
{
  static std::tuple<const byte *, TFirst, TRest...> Extract(const byte *p_current)
  {
    TFirst first_value;
    TRest... rest_values; // Not working.

    std::tie(p_current, first_value) = Extractor<TFirst>::Extract(p_current);
    std::tie(p_current, rest_values...) = Extractor<TRest...>::Extract(p_current);

    return std::make_tuple(p_current, first_value, rest_values...);
  }
};

template<class T>
struct Extractor<T>
{
  static std::tuple<const byte *, T> Extract(const byte *p_current)
  {
    return std::make_tuple(p_current + sizeof(T), *reinterpret_cast<const T *>(p_current));
  }
};

它不會編譯,因為“在這種情況下無法擴展參數包”。 我聽說函數模板不能部分專業化,所以我使用結構。 如何使其運作?

這是一個純C ++ 11解決方案:

#include <tuple>
#include <type_traits>

typedef unsigned char byte;

template <class Type>
void ExtractValue(const byte*& p_current, Type& value)
{
    value = *reinterpret_cast<const Type*>(p_current);
    p_current += sizeof(Type);
}

template <size_t index, class... Types>
typename std::enable_if<index == sizeof...(Types)>::type
ExtractImpl(const byte*& p_current, std::tuple<Types...>& values)
{}

template <size_t index, class... Types>
typename std::enable_if<(index < sizeof...(Types))>::type
ExtractImpl(const byte*& p_current, std::tuple<Types...>& values)
{
    ExtractValue(p_current, std::get<index>(values));
    ExtractImpl<index + 1>(p_current, values);
}

template <class... Types>
std::tuple<Types...> Extract(const byte *p_current)
{
    std::tuple<Types...> values;

    ExtractImpl<0>(p_current, values);

    return values;
}

此解決方案不會將p_current添加到返回的元組中,但是您可以輕松地對其進行修復。

我發現這樣簡單得多,根本沒有模板遞歸( 實時示例 ):

template<typename T>
T extract(const byte *&p_current)
{
   const byte *p = p_current;
   p_current += sizeof(T);
   return *reinterpret_cast<const T*>(p);
}

template<typename... T>
std::tuple<T...> Extract(const byte *&p_current)
{
   return std::tuple<T...>{extract<T>(p_current)...};
}

不幸的是,由於持續存在的錯誤 ,gcc以相反的順序(從右到左)評估tuple的列表初始化的參數,因此您將在實際示例中看到它僅在clang中可以正常工作。 但是,如果僅在gcc調用Extract之前翻轉參數列表T... ,仍然可以應用此解決方案。 我寧願保持簡單,然后等待錯誤修復。

TRest... rest_values; // Not working.

這是無效的,您不能聲明類型為TRest...的變量,因為這不是類型(它是TRest...類型)。

Yakk在上面的評論是一種更干凈的解決方案,但是如果您只想使用元組,則可以執行以下操作:

std::tuple<TRest...> rest_values;

然后修改以下代碼以使用該代碼。

您不能輕易將std::tie與元組元素一起使用(您將需要C ++ 14的apply()函數),所以我可以這樣做:

  static std::tuple<const byte *, TFirst, TRest...> Extract(const byte *p_current)
  {
    TFirst first_value;
    std::tuple<const char*&, TRest...> rest_values{ p_current, TRest{}... };

    std::tie(p_current, first_value) = Extractor<TFirst>::Extract(p_current);
    rest_values = Extractor<TRest...>::Extract(p_current);

    return std::make_tuple(p_current, first_value, cdr(rest_values));
  }

其中cdr()是Lisp CDR操作,它將返回列表的第一個元素以外的所有內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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