2016-04-30 49 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

你是什麼意思*連接*? – Zereges

+0

沒關係。解決方案與函數的語義無關。對於你的信息,如果DATA是c字符串,A :: DATA =「abc」,A :: SIZE = 3,則連接(A :: DATA,A :: SIZE,A :: DATA,A :: SIZE)會與數據「abcabc」和大小爲6的陣列。 – user4663214

回答

1

懶惰的解決方案使用std::tuple

  • 使DATASIZE的參數組中的每個元素的元組,
  • 使用std::tuple_cat壓扁元組到一個大的元組列表,
  • 申請通過展開std::index_sequence中的索引列表將結果元組的元素設置爲Concatenation

在下面的代碼,測試工具是比實際的解決方案更長:

#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(就像基於元組的解決方案一樣)。

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

如果你知道更優雅的方式,請讓我知道。

編輯

一個小更優雅的方式是保持函數參數計數作爲模板參數...

+0

一般來說,回答您自己的問題不是習慣做法。也許你應該把它添加到你原來的文章中,然後改變你的文章以尋求一個更優雅的解決方案。 – 2016-05-01 00:36:07

+0

@William問自己的問題沒有錯。 – Zereges

+0

@Zerges,當然。那麼回答它呢?特別是甚至沒有給它一天?此外,我不記得稱它是「錯誤的」。 – 2016-05-01 16:26:11

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

你確實改變了AutoConcatenation的聲明:'template MyArray AutoConcatenation();',沒有輸入作爲參數...我不認爲你的解決方案更優雅,因爲增量連接,這對我來說分配太多了。我應該提到,'Concatenation(...)'方法只執行一次分配,不管參數是否計數...我的意思是重新排序輸入參數或以某種方式以上述方式擴展它們...... – user4663214

+0

好,但是修改是顯而易見的(就宣言而言)。至於你的其他問題:好吧,那不是OP的一部分。無論如何,這些也可以通過簡單的修改來修復。就像我說的,我爲一個工作示例提供了一個簡單的實現。你以任何你想要的方式實現它,並且你應該在Concatenate()的實現中關注分配問題......如果你不能,那麼這是糟糕的設計。 – 2016-05-02 12:31:13