静态变量

时间:2016-04-30 21:50:57

标签: c++ templates variadic-templates

我正在考虑以下问题:

让我们为以下列方式定义的合并数组提供合并函数:

// input is (const void*, size_t,  const void*, size_t,...)
template<typename...ARGS>
MyArray Concatenation(ARGS...args)

让我们有几个具有静态属性的结构

struct A { static void* DATA; static size_t SIZE; };
struct B { static void* DATA; static size_t SIZE; };
struct C { static void* DATA; static size_t SIZE; };

我想要一个方法:

template<typename...ARGS>
MyArray AutoConcatenation();

其中ARGS应该与提到的静态接口结构化。 以下方法应具有相同的输出:

AutoConcatenation<A, B, C>();
Concatenation(A::DATA, A::SIZE, B::DATA, B::SIZE, C::DATA, C::SIZE);

我的问题是如何实现参数包扩展。

我试过了:

// not working
template<typename...ARGS>
MyArray AutoConcatenation()
{
  return Concatenation((ARGS::DATA, ARGS::SIZE)...);
}

扩展怎么样

ARGS::DATA... // Correct expansion of pointers
ARGS::SIZE... // Correct expansion of sizes
(ARGS::DATA, ARGS::SIZE)... // Seems to be expansion of sizes

只需咨询顾问。我正在寻找 AutoConcatenation 方法的实现,不是为了重新声明,也不是为了重新声明以前的代码,谢谢。

3 个答案:

答案 0 :(得分:1)

使用std::tuple的惰性解决方案:

  • 为参数包的每个元素创建DATASIZE元组,
  • 使用std::tuple_cat
  • 将元组列表展平为一个大元组
  • 通过展开Concatenation中的索引列表,将生成的元组元素应用于std::index_sequence

在以下代码中,测试工具比实际解决方案更长:

#include <cstddef>
#include <tuple>
#include <utility>
#include <iostream>
#include <typeinfo>
#include <type_traits>

struct MyArray { };
template<class... ARGS> MyArray Concatenation(ARGS... args) 
{
   // Just some dummy code for testing.
   using arr = int[];
   (void)arr{(std::cout << typeid(args).name() << ' ' << args << '\n' , 0)...};
   return {}; 
}

struct A { static void* DATA; static std::size_t SIZE; };
struct B { static void* DATA; static std::size_t SIZE; };
struct C { static void* DATA; static std::size_t SIZE; };

// Also needed for testing.
void* A::DATA;
std::size_t A::SIZE;
void* B::DATA;
std::size_t B::SIZE;
void* C::DATA;
std::size_t C::SIZE;


// The useful stuff starts here.

template<class T, std::size_t... Is> MyArray concat_hlp_2(const T& tup, std::index_sequence<Is...>)
{
   return Concatenation(std::get<Is>(tup)...);
}

template<class T> MyArray concat_hlp_1(const T& tup)
{
   return concat_hlp_2(tup, std::make_index_sequence<std::tuple_size<T>::value>{});
}

template<class... ARGS> MyArray AutoConcatenation()
{
   return concat_hlp_1(std::tuple_cat(std::make_tuple(ARGS::DATA, ARGS::SIZE)...));
}


int main()
{
   AutoConcatenation<A, B, C>();
}

如果你想避免std::tuplestd::tuple_cat(在编译时间方面可能很重),这里可以使用索引到数组中。

测试代码保持不变,这只是多汁的东西:

template<std::size_t Size> const void* select(std::false_type, std::size_t idx,
   const void* (& arr_data)[Size], std::size_t (&)[Size])
{
   return arr_data[idx];
}

template<std::size_t Size> std::size_t select(std::true_type, std::size_t idx,
   const void* (&)[Size], std::size_t (& arr_size)[Size])
{
   return arr_size[idx];
}

template<std::size_t... Is> MyArray concat_hlp(std::index_sequence<Is...>, 
   const void* (&& arr_data)[sizeof...(Is) / 2], std::size_t (&& arr_size)[sizeof...(Is) / 2])
{
   return Concatenation(select(std::bool_constant<Is % 2>{}, Is / 2, arr_data, arr_size)...);
}

template<class... ARGS> MyArray AutoConcatenation()
{
   return concat_hlp(std::make_index_sequence<sizeof...(ARGS) * 2>{}, {ARGS::DATA...}, {ARGS::SIZE...});
}

再一个索引序列是原始参数包大小的两倍,但我们构建了DATASIZE的单独数组,然后使用标记调度从一个或另一个中选择元素,具体取决于当前指数的平价。

这可能看起来不像以前的代码那么好,但它并不涉及任何模板递归(据我所知,现代编译器中使用编译器内在函数实现std::make_index_sequence)并减少了模板实例化的数量,因此编译速度应该更快。

通过使用指向静态成员的指针数组,可以使select帮助器成为constexpr,但实际上这是不必要的。我已经测试了MSVC 2015 U2,Clang 3.8.0和GCC 6.1.0,他们都对此进行了优化,直接调用了Concatenation(就像基于元组的解决方案一样)。

答案 1 :(得分:0)

这是可能的解决方案:

enum Delimiters { Delimiter };
const void* findData(size_t count) { return nullptr; }

template<typename...ARGS>
const void* findData(size_t count, size_t, ARGS...args)
{
    return findData(count, args...); 
}

template<typename...ARGS>
const void* findData(size_t count, const void* data, ARGS...args) 
{ 
    return count ? findData(count - 1, args...) : data; 
}

template<typename...ARGS>
MyArray reordered(size_t count, Delimiters, ARGS...args) 
{
    return Concatenate(args...); 
}

template<typename...ARGS>
MyArray reordered(size_t count, const void* size, ARGS...args) 
{ 
    return reordered(count, args...); 
}

template<typename...ARGS>
MyArray reordered(size_t count, size_t size, ARGS...args) 
{ 
    return reordered(count + 1, args..., findData(count, args...), size); 
}

template<typename...ARGS>
MyArray AutoConcatenate()
{
    return reordered(0, ARGS::LAYOUT_SIZE..., ARGS::LAYOUT..., Delimiter);
}

如果您了解更优雅的方式,请告诉我。

修改

一个更优雅的方法是将函数参数 count 保持为模板参数...

答案 2 :(得分:0)

我认为以下更优雅,它说明了常见的递归解包模式。最后,它不会对内存布局执行任何巫术,并且尝试在C ++泛型编程中使用惯用语。

#include <iostream>
#include <string>
using namespace std;

// Handle zero arguments.
template <typename T = string>
T concat_helper() { return T(); }

// Handle one pair.
template <typename T = string>
T concat_helper(const T &first, size_t flen) { return first; }

// Handle two or more pairs. Demonstrates the recursive unpacking pattern
// (very common with variadic arguments).
template <typename T = string, typename ...ARGS>
T concat_helper(const T &first, size_t flen,
                const T &second, size_t slen,
                ARGS ... rest) {
  // Your concatenation code goes here. We're assuming we're
  // working with std::string, or anything that has method length() and
  // substr(), with obvious behavior, and supports the + operator.
  T concatenated = first.substr(0, flen) + second.substr(0, slen);
  return concat_helper<T>(concatenated, concatenated.length(), rest...);
}

template <typename T, typename ...ARGS>
T Concatenate(ARGS...args) { return concat_helper<T>(args...); }

template <typename T>
struct pack {
  T data;
  size_t dlen;
};

template <typename T>
T AutoConcatenate_helper() { return T(); }

template <typename T>
T AutoConcatenate_helper(const pack<T> *packet) {
  return packet->data;
}

template <typename T, typename ...ARGS>
T AutoConcatenate_helper(const pack<T> *first, const pack<T> *second,
                         ARGS...rest) {
  T concatenated = Concatenate<T>(first->data, first->dlen,
                                  second->data, second->dlen);
  pack<T> newPack;
  newPack.data = concatenated;
  newPack.dlen = concatenated.length();

  return AutoConcatenate_helper<T>(&newPack, rest...);
}

template <typename T, typename ...ARGS>
T AutoConcatenate(ARGS...args) {
  return AutoConcatenate_helper<T>(args...);
}

int main() {
  pack<string> first;
  pack<string> second;
  pack<string> third;
  pack<string> last;

  first.data = "Hello";
  first.dlen = first.data.length();

  second.data = ", ";
  second.dlen = second.data.length();

  third.data = "World";
  third.dlen = third.data.length();

  last.data = "!";
  last.dlen = last.data.length();

  cout << AutoConcatenate<string>(&first, &second, &third, &last) << endl;

  return 0;
}

我们既不会根据需要更改Concatenate<>()的声明,也不会更改AutoConcatenate<>()的声明。我们可以像我们一样自由地实现AutoConcatenate<>(),并且我们假设有一些Concatenate<>()的实现(我们为一个工作示例提供了一个简单的实现)。