2013-08-01 4 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++ 컴파일러 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

내가 그것을 해결하기 위해 시도하는 방법이다 주셔서 감사합니다. 함수 저장소가 그것을해야합니다. 하나의 (두 번 오버로드 된) 도우미 함수를 사용합니다.

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

함수 저장소는 인수의 varialbe 수가 저장되어야하는 메모리를 가리키는 포인터를 사용합니다.

포인터가 동적으로 할당 된 메모리를 가리킬 수 있거나 크기가 sizeof...(Params) 인 구조체를 가리킬 수 있습니다. 정확히 어떤 desiared 크기가 이러한 구조는 템플릿 메타 프로그래밍을 사용하여 구성 할 수 있습니다

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

template <> 
struct allocatorStruct<1> {}; 

나는 STANDART은 마이크로 소프트가 아닌 다른 컴파일러를 컴파일하는 방법이나 말하는 것을 확실하지 않다. 하지만 내 컴파일러를 사용하여 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 뿐만 아니라 non-const 인스턴스도 포함합니다.

관련 문제