2016-04-30 2 views
1

Я думаю о следующей проблеме:параметров для статических переменных

Давайте функцию слияния для слияния массивов, определенных в следующим образом:

// 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, а не для его переоформления или для повторного оформления предыдущего кода, спасибо.

+0

Что вы подразумеваете под * Concatenation *? – Zereges

+0

Это не имеет значения. Решение не зависит от семантики функций. Для вашей информации, если DATA будет c строк, A :: DATA = "abc", A :: SIZE = 3, Concatenation (A :: DATA, A :: SIZE, A :: DATA, A :: SIZE) будет быть массивом с данными «abcabc» и размером 6. – user4663214

ответ

1

Ленивый решение с использованием std::tuple:

  • сделать кортеж DATA и SIZE для каждого элемента параметра пакета,
  • придавить список кортежей в один большой кортеж с помощью 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::tuple и std::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...}); 
} 

Опять последовательность индексов в два раза размер исходного параметра пакета, но мы строим отдельные массивы DATA и SIZE, а затем использовать тег диспетчеризации для выбора элементов из одного или другого в зависимости от четности текущего индекса.

Это может выглядеть не так хорошо, как предыдущий код, но он не включает рекурсию шаблона (std::make_index_sequence реализован с использованием встроенных компиляторов в современных компиляторах, насколько я знаю) и сокращает количество экземпляров шаблонов, поэтому его нужно быстрее компилировать.

Помощник select может быть сделан constexpr с использованием массивов указателей на статические элементы, но на практике это оказывается излишним. Я тестировал MSVC 2015 U2, Clang 3.8.0 и GCC 6.1.0, и все они оптимизировали это для прямого вызова Concatenation (как и для решения на основе кортежей).

+0

Ницца. Спасибо. – user4663214

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); 
} 

Если вы знаете более элегантный способ, пожалуйста, дайте мне знать.

EDIT

Один немного более элегантный способ сохранить аргумент функции количество в качестве параметра шаблона ...

+0

В общем, ответ на ваш собственный вопрос не является обычным явлением. Возможно, вам стоит просто добавить это в свой оригинальный пост и изменить свой пост, чтобы попросить более элегантное решение. –

+0

@William В этом вопросе нет ничего плохого. – Zereges

+0

@Zerges, конечно. Как насчет ответа? Особенно, не давая ему даже дня? Кроме того, я не помню, чтобы называть это «неправильным». –

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<>() (мы предоставили простой пример для рабочего примера).

+0

Вы изменили объявление AutoConcatation: 'template MyArray AutoConcatenation();', нет ввода в качестве аргумента ... Я не думаю, что ваше решение более элегантное, из-за инкрементного конкатенация, которая слишком много выделяет для меня. Я должен упомянуть, что метод 'Concatenation (...)' выполняет только одно распределение, независимо от количества аргументов ... Я хотел просто изменить порядок входных аргументов или каким-то образом расширить их описанным выше способом ... – user4663214

+0

Хорошо, но модификации очевидны (в отношении декларации). Что касается ваших других проблем: ну, это не было частью ОП. Во всяком случае, они могут быть исправлены и с легкими изменениями. Как я уже сказал, я представил простую реализацию для рабочего примера. Вы реализуете его любым способом, и вы должны заботиться о проблемах с распределением в рамках реализации Concatenate() ... если вы не можете, то это плохой дизайн. –

Смежные вопросы