2013-08-01 7 views
6

この関数の関数ポインタと引数を格納できるテンプレートクラスを作成して、この引数で関数を後で呼び出せるようにしたいと考えています。バリデーションテンプレート引数を使用して可変数の引数を保存する方法は?

私はこれを普遍的に書いており、引数の型や番号には依存しません。私はそれが可能であれば安全に、このクラスタイプの少なくともパブリックインターフェイスを持っていると思い

template<class T, typename... Params> 
class LazyEvaluation { 
private: 
    // Function to be invoked later 
    T (*f)(Params...); 
    // Params for function f 
    Params... storedParams; // This line is not compilable! 
    bool evaluated; 
    T result; 
public: 
    // Constructor remembers function pointer and parameters 
    LazyEvaluation(T (*f)(Params...),Params... params) 
    : f(f), 
    storedParams(params) //this line also cannot be compiled 
    {} 
    // Method which can be called later to evaluate stored function with stored arguments 
    operator T&() { 
      // if not evaluated then evaluate 
      if (! evaluated) { 
        result = f(storedParams...); 
        evaluated = true; 
      } 
      return result; 
    } 
} 

:ここ

は、C++ 11の可変長引数テンプレートを使用してアイデアのscatchです。この仕事を少なくとも何とかすることが重要です。

私は何とか可変数の引数を保存することができました。しかし、私は関数fにそれらを渡すことができませんでした。私はそれを答えに書いていきますが、あなたが私の醜い作業ではない試みを見る前に、自分の解決策について考えてください。

私は、上記のコードをMicrosoft Visual C++ Compiler 11月2012 CTP(v120_CTP_Nov2012)でコンパイルしていますが、コンパイラに依存しないソリューションがあれば最適です。

parametrパックがrecursivle拡大することができ、各parametrが保存:

はここ

+3

私はこれが重複していると思います:http://stackoverflow.com/questions/14833129/a-clean-way-to-store-a-function-and-its-arbitrary-type-arbitrary-number-argum – hmjd

+4

それらを 'std :: tuple 'に保存してください... – kennytm

+0

@KennyTMこれは答えになるはずです。 – Angew

答えて

0

が、私はそれを解決しようとした方法です、ありがとうございました。ファンクションストアがそれを行うはずです。 1つ(2倍のオーバーロード)ヘルパー機能を使用します。

template<typename T> 
void storeHelperFunction(void*& memory, T last) { 
    *((T*)memory) = last; 
    memory = (void*)((char*)memory + sizeof(T)); 
} 

template<typename T, typename... Params> 
void storeHelperFunction(void*& memory, T first, Params... rest) { 
    storeHelperFunction(memory, first); 
    storeHelperFunction(memory, rest...); 
} 

template<typename... Params> 
void store(void* memory, Params... args) { 
    // Copy of pointer to memory was done when passing it to this function 
    storeHelperFunction(memory, args...); 
} 

ファンクションストアは、引数の可変数が保存されるはずのメモリへのポインタをとります。

ポインターは、動的に割り当てられたメモリを指しているか、サイズがsizeof...(Params)に等しい構造を指している可能性があります。正確にどのdesiared大きさを持っている このような構造は、テンプレートメタプログラミングを使用して構築することができます。

template <int N> 
struct allocatorStruct { 
    char byte1; 
    allocatorStruct<N-1> next; 
}; 

template <> 
struct allocatorStruct<1> {}; 

私はスタンダールは、Microsoft以外のコンパイラはそれをコンパイルする方法か、言っていることを確認していません。しかし、私のコンパイラを使用すると、sizeof(allocatorStruct)は1以上の任意のNに対してNと等しくなります。

したがって、allocatorStruct<sizeof...(Params)>はParamsと同じサイズです。

Paramsと同じサイズのものを作成する別の方法は、タイプchar [sizeof...(Params)]を使用することです。これは、引数としてそのような配列を渡そうとすると、コンパイラがこの配列へのポインタだけを渡すという欠点があります。 そのため、allocatorStruct<sizeof...(Params)>を使用する方が良いです。

そして今、主なアイデア:T (*)(allocatorStruct<sizeof...(Params)>):機能を保存するとき

我々はそれをキャストすることができます。 関数の引数を保存するときに、構造体allocatorStruct<sizeof...(Params)>に保存することができます。

引数のサイズは同じです。関数ポインタは、その関数が指し示す関数の型に関するものですが、そのデータを正しく取得します。

少なくとも私は望んでいました。呼び出し規約に応じて、左から右の節約引数と右から左への引き渡しの違いのために、渡された引数が並べ替えられたり間違ったりすることがあります。しかし、そうではありませんでした。 __cdecl呼び出し規約を使用すると、最初の引数のみが渡され、もう一方は失われました。他の呼び出し規約では、プログラムは機能しなくなりました。

私は(スタックに)それをデバッグし、メモリ内のデータを探して多くの時間を費やすことはありませんでした。少なくとも行くのは正しいですか?あなたが本当に一度だけ(なまけ)関数を評価するクラスを作成したい場合は

+0

メモリの配置に問題があります。あなたは、 'memory + sizeof(T)'が引数リストの次の要素の良い整列であることを知ることはできませんが、 – selalerer

0

は、単に可変引数テンプレートを使用して、ラムダ式に

// Some function. 
int add(int a, int b) { 
    return a + b; 
} 

auto lazyFunc = [] { return add(1, 2); }; 

std::cout << lazyFunc() << std::endl; // Evaluate function and output result. 

を使用するには、次のコードのような何かを行うことができます。

私はまた、あなたが新しいインスタンスにパラメータが変化するたびに作成する必要がないことのようなクラスを作りました。私はstd::tupleを使って与えられた引数を格納し、以前に与えられた引数と比較します。引数が異なる場合、関数は再評価されます。

機能が回され、私は生の関数ポインタ(不潔)で動作する必要はありませんのでstd::functionラッパーを使用して保存されます。

#include <iostream> 
#include <functional> 
#include <utility> 
#include <tuple> 

template <typename T> 
class LazyEvaluation {}; 

template <typename ReturnType, typename... Params> 
class LazyEvaluation<ReturnType(Params...)> { 
private: 
    std::function<ReturnType(Params...)> func_; 
    ReturnType result; 
    std::tuple<Params...> oldParams; // Contains the previous arguments. 
public: 
    explicit LazyEvaluation(std::function<ReturnType(Params...)> func) 
     : func_(std::move(func)) {} 
    template <typename... Args> 
    ReturnType operator() (Args&&... args) { 
     auto newParams = std::make_tuple(std::forward<Args>(args)...); 

     // Check if new arguments. 
     if (newParams != oldParams) { 
      result = func_(std::forward<Args>(args)...); 
      oldParams = newParams; 
      std::cout << "Function evaluated" << std::endl; 
     } 

     std::cout << "Returned result" << std::endl; 
     return result; 
    } 
}; 

int main() { 
    auto f = [] (int a, int b) { 
     return a + b; 
    }; 

    // Specify function type as template parameter. 
    // E.g. ReturnType(Param1Type, Param2Type, ..., ParamNType) 
    LazyEvaluation<int(int, int)> ld(f); 

    std::cout << ld(1, 2) << std::endl; 
    std::cout << ld(1, 2) << std::endl; 
    std::cout << ld(3, 4) << std::endl; 
} 

出力:

template <std::size_t... I> struct index_sequence {}; 
template <std::size_t N, std::size_t... I> 
struct make_index_sequence : public make_index_sequence<N-1, N-1, I...> {}; 
template <std::size_t... I> 
struct make_index_sequence<0, I...> : public index_sequence<I...> {}; 

とアンパックタプル引数を持つ関数を呼び出す:

template <typename Function, typename... Types, std::size_t... I> 
auto apply_(Function&& f, const std::tuple<Types...>& t, index_sequence<I...>) 
    -> decltype(std::forward<Function>(f)(std::get<I>(t)...)) { 
    return std::forward<Function>(f)(std::get<I>(t)...); 
} 

template <typename Function, typename... Types> 
auto apply(Function&& f, const std::tuple<Types...>& t) 
    -> decltype(apply_(f, t, make_index_sequence<sizeof...(Types)>())) { 
    return apply_(f, t, make_index_sequence<sizeof...(Types)>()); 
} 

この

Function evaluated 
Returned result 
3 
Returned result 
3 
Function evaluated 
Returned result 
7 
0

は、可変引数のインデックスパックを形成するための標準的な機械を考えますかなりstrです私はそれがデフォルト・構成可能タイプである必要はないように、評価の結果を格納するために労働組合と配置newを使用しました

template<typename Function, typename... Params> 
class LazyEvaluation { 
private: 
    typedef decltype(std::declval<Function>()(std::declval<Params>()...)) result_type; 
    // Function to be invoked later 
    Function f; 
    // Params for function f 
    std::tuple<Params...> storedParams; 
    mutable bool evaluated; 
    union { 
    std::aligned_storage<sizeof(result_type)> space; 
    mutable result_type result; 
    }; 

    // Method which can be called later to evaluate stored function with stored arguments 
    void evaluate() const { 
    // if not evaluated then evaluate 
    if (! evaluated) { 
     new (&result) result_type{apply(f, storedParams)}; 
     evaluated = true; 
    } 
    } 

public: 
    // Constructor remembers function pointer and parameters 
    LazyEvaluation(Function f, Params... params) 
    : f(std::move(f)), 
     storedParams(std::move(params)...), 
     evaluated(false) 
    {} 
    ~LazyEvaluation() { 
    if (evaluated) 
     result.~result_type(); 
    } 

    operator result_type&() { 
    evaluate(); 
    return result; 
    } 

    operator const result_type&() const { 
    evaluate(); 
    return result; 
    } 
}; 

template <typename Function, typename... Params> 
LazyEvaluation<Function, Params...> 
make_lazy(Function&& f, Params&&... params) { 
    return {std::forward<Function>(f), std::forward<Params>(params)...}; 
} 

、およびconst LazyEvaluatorを変換することができるように、いくつかのトリックmutable:aightforward非constインスタンスなどがあります。

関連する問題