2017-11-14 5 views
0

私は少し書く/ライブラリを最適化したいと思っています.2週間後、私は多少立ち往生しています。そのような可能性があります。 (私はメタプログラミングの経験はあまりありません)テンプレート - メタプログラミング、constexprまたはマクロを使用してコンパイル時にデータ構造を構築する

私の目標はもちろん、プログラマーがうまくいけばコードを編集する必要があるように、特定の計算をコンパイラで行うことですプログラムのある時点で、コンパイラにすべての定型文を「作成」させる。私はマクロを使って何をしたいのかを、どうやってどうやってできるか分かりやすく考えていますが、可能ならばテンプレートを使ってやりたいと思っています。

私の目標は 私は、使用しているプログラマが派生できるクラスがあると言います。そこには、基本クラスがそれらに対して操作を行うことができるように、何らかの方法で登録したい複数の着信および発信データ型を持つことができます。

class my_own_multiply : function_base { 
    in<int> a; 
    in<float> b; 
    out<double> c; 

    // ["..."] // other content of the class that actually does something but is irrelevant 
    register_ins<a, b> ins_of_function; // example meta-function calls 
    register_outs<c> outs_of_function; 
} 

私が今まで持っていたメタコードはこれです:(しかし、それは完全なジェット作業/ではありません)

template <typename... Ts> 
struct register_ins { 
    const std::array<std::unique_ptr<in_type_erasured>, sizeof...(Ts)> ins; 

    constexpr std::array<std::unique_ptr<in_type_erasured>, sizeof...(Ts)> 
    build_ins_array() { 
     std::array<std::unique_ptr<in_type_erasured>, sizeof...(Ts)> ins_build; 
     for (unsigned int i = 0; i < sizeof...(Ts); ++i) { 
     ins_build[i] = std::make_unique<in_type_erasured>(); 
     } 
    return ins_build; 
    } 

    constexpr register_ins() : ins(build_ins_array()) { 
    } 

    template <typename T> 
    T getValueOf(unsigned int in_nr) { 
    return ins[in_nr]->getValue(); 
    } 
}; 

ご覧の通り、私は私のメタテンプレートコードを呼び出したいですさまざまな数のインがあります。 (プログラマーが多くの人を好きに置くことができるという感覚で可変ですが、実行時には変更されないため、コンパイル時に "焼き付け"できます)

メタコードは作成中配列の長さはinsの数であり、すべてのフィールドがmy_own_multiplyクラスの元の値を指すように初期化されます。基本的には常に正しいサイズを持つ索引付け可能なデータ構造を与えます。そして、私はfunction_baseクラスからアクセスして、特定の関数のためにすべての関数を使用することができます。

私はこれをどうやって行うのか見てきましたが、コンパイル時にこの配列を "作成"することが実際には許されないかもしれないという気持ちが現れています。私はそれらを突然変異させることができるように、非静的で非constである。私の側からは、とにかくコンスタントである必要はありませんでしたが、私の作者は、彼らが自由であることを好まないようです。 constが必要なのは、ポインタを持つ配列だけです。しかし、おそらくconstexprを使うことで、私はそれらをconstにすることができますか?

私は何を得られないのかを明確にします: 私はメタスタッフ構造の "インスタンス"を作成しようとしていますが、それはすべての種類のconst、constexprなどを期待するため失敗します。 。しかし、私はそれらの変数の大部分を変異させる必要があるので、私はそれらを望んでいません。コンパイル時にすでに正しいサイズの配列を作成するには、このメタデータだけが必要です。しかし、私はこれを実現するためにすべてを静的にしなければならないことを犠牲にしたくない。このような言葉の下でもこれは可能ですか?

+2

あなたが欲しいものを理解しようとする問題。私はあなたが他のメタプログラミングライブラリーについて最初に学ぶ方が良いと思う。ブースト。つまり、「タイプ消去された」ものは間違っているようだ。なぜあなたはそれを気にしていますか?私は 'std :: array'ではなく' std :: tuple'を見ることを期待しています。 – MSalters

+0

タイプ消しゴムは今のところはうっ血だから、私はそれが怪しいように見えるのではない。重要なのは、私が最後に基本クラスからアクセスできるデータ構造を作成する部分です。 タプルが必要なのは何ですか?なぜそれを期待していますか? – Andimanos

+0

どのバージョンのC++を使用できますか? C++ 14以降であれば、 'constexpr'を持つ関数は読みやすく、保守が容易なコンパイル時の計算を提供することができます(旧形式のテンプレートメタプログラミングの一部を置き換える)。興味があれば、私は答えとして例を用意することができます。 – Julius

答えて

0

私は(も、あなたの例では、そのstd::unique_ptrに関する)あなたが考えているすべてのものを得ることはありませんが、多分これは役立ちます:C++ 14(またはC++ 11からスタート

を、それがあります厳密に制限されています)コンパイル時に評価できる関数constexprを書くことができます。前提条件として(単純な言葉で)、 "呼び出し側が渡した"引数はすべてconstexprでなければなりません。コンパイラがコンパイル時の計算の結果によってその "呼び出し"を置き換えるように強制する場合は、結果をconstexprに代入する必要があります。

通常の関数を書く(ちょうどconstexprが追加されている)ので、読みやすいコードを書くことができます。さらに、同じコードをコンパイル時の計算と実行時の計算の両方に使用できます。

C++ 17例(stdからいくつかのものがちょうどconstexpr修飾子が欠落しているが、同様の事が、C++ 14で可能です):私は本当のを持っている

http://coliru.stacked-crooked.com/a/154e2dfcc41fb6c7

#include <cassert> 

#include <array> 

template<class T, std::size_t N> 
constexpr std::array<T, N> multiply(
    const std::array<T, N>& a, 
    const std::array<T, N>& b 
) { 
// may be evaluated in `constexpr` or in non-`constexpr` context 

// ... in simple man's words this means: 
// inside this function, `a` and `b` are not `constexpr` 
// but the return can be used as `constexpr` if all arguments are `constexpr` for the "caller" 
    std::array<T, N> ret{}; 
    for(size_t n=0; n<N; ++n) ret[n] = a[n] * b[n]; 
    return ret; 
} 

int main() { 
    {// compile-time evaluation is possible if the input data is `constexpr` 
    constexpr auto a = std::array{2, 4, 6}; 
    constexpr auto b = std::array{1, 2, 3}; 
    constexpr auto c = multiply(a, b);// assigning to a `constexpr` guarantees compile-time evaluation 

    static_assert(c[0] == 2); 
    static_assert(c[1] == 8); 
    static_assert(c[2] == 18); 
    } 
    {// for run-time data, the same function can be used 
    auto a = std::array{2, 4, 6}; 
    auto b = std::array{1, 2, 3}; 
    auto c = multiply(a, b); 

    assert(c[0] == 2); 
    assert(c[1] == 8); 
    assert(c[2] == 18); 
    } 
    return 0; 
} 
+0

これは私が探していた解決策ではありませんが、自分の解決策を見つけるために欠けていたものを見つけるのを助けました。有難うございます。私はまだそれを正解とするべきでしょうか? – Andimanos

+0

@Andimanos:わかりません。あなたの解がきちんと説明できればあなた自身の質問に答えることができ、答えを受け入れることができます。それが努力の価値がないなら、私の答えを受け入れることは、少なくともあなたの問題が解決されたことを他の人に示すでしょう。 – Julius

関連する問題