2014-10-22 1 views
3

단일 테이블에서 모든 유형의 함수를 호출하려고합니다. (반환 유형은 모두 무효라고 생각하십시오.)매개 변수 서명에 관계없이 함수 테이블을 만들 수 있습니까?

내가 말하는 바를 설명하기 위해 분명히 작동하지 않는 코드가 있습니다.

#include <iostream> 
#include <map> 
#include <functional> 

void foo(int x){std::cout<<x;} 
void bar(){std::cout<<"bar";} 


std::map<std::string, std::function<void()>> map = 
{ 
    {"foo", foo}, 
    {"bar", bar} 
}; 

int main() 
{ 
    map["foo"](2); 
    map["bar"](); 
} 

저는 C 스타일 솔루션에 반대하지 않았습니다.

+2

[여기]와 (http://coliru.stacked-crooked.com/a/74fd8557676d6d39)? –

+0

어떻게 이러한 기능을 호출 하시겠습니까? 확실히 그 시간에 매개 변수를 알게 될 것입니다. 어떤 경우에는 각 유형의 기능 서명에 대해 별도의 맵을 사용하지 않는 것이 좋습니다. 함수 유형은 정적 매개 변수를 포함하는 템플릿 매개 변수 인 템플릿 기반 클래스를 사용할 수 있습니다. –

+0

@NeilKirk 나는 그것도 고려하고있다. 나는 간단한 언어에 대한 특별 통역사를 쓰고있다. 값은 사용자로부터옵니다. – manasij7479

답변

1

당신은 에 같은 가변 인자 함수 포인터 이전 스타일 C의 함수 포인터를 선언 할 수 :

foo(...); 
bar(...); 

std::map<void(*)(...)> map = 
{ 
    {"foo", foo}, 
    {"bar", bar} 
}; 

을하지만 va_args와 가변 호출 규칙, va_start를 등을 수행 할 필요가 foo는 바 당신은 할 수있다 목록에서 C POD 만 가져옵니다. 혼전의 가치가 있는지 모르겠다. 호출하는 메소드는 여전히 어떤 수의 args를 전달해야 하는지를 알아야한다.

디자인을 다시 생각할 수있는 것처럼 보입니다.

예를 들어 이것은 일종의 CLI를위한 명령 표일 경우 std::vector<std::string>을 각 가능한 명령에 전달하고 벡터의 크기가 올바른지 알아내는 것이 좋습니다().

0

약간의 접근 방식을 변경했는데, 이것이 단지 예일 뿐이라는 것을 알고 있습니다. 나는 이렇게 컴파일하지 않을 것이라고 확신하지만, 내가 생각한 것을 생각해 낼 것입니다. 추가 구조체에 함수를 등록한 다음 해당 구조체를 호출하여 매개 변수를 전달할 수 있습니다. 당신은 완전히 형식 시스템을 버리지 경우 정확히 바로 모든 곳에서 모든 유형을 얻을로

struct Funcs 
{ 
    std::function<void(int)> _f1; 
    std::function<void()> _f2; 

    template<typename args...> 
    void call(std::string&& f_name, args...) 
    { 
     if(f_name == "foo") 
      _f1(std::forward(args)...) 
     if(f_name == "bar") 
      _f2(std::forward(args)...) 
    } 
} 


int main() 
{ 
    Funcs f; 
    f.call("foo", 2); 
} 
1

, 당신은 한 boost::any를 사용할 수 있습니다. 네,

class Functions 
{ 
public: 
    template <typename... T> 
    void add_function(const std::string& name, void (*f)(T...)) 
    { 
     fs[name] = std::function<void(T...)>{f}; 
    } 


    template <typename... T> 
    void add_function(const std::string& name, std::function<void(T...)> f) 
    { 
     fs[name] = f; 
    } 

    template <typename... T> 
    void call(const std::string& name, T... args) 
    { 
     auto it = fs.find(name); 
     if (it != fs.end()) { 
      auto f = boost::any_cast<std::function<void(T...)>>(&it->second); 
      if (f) { 
       (*f)(args...); 
      } 
      else { 
       std::cout << "invalid args for " << name << std::endl; 
      } 
     } 
     else { 
      std::cout << "not found: " << name << std::endl; 
     } 
    } 

private: 
    std::map<std::string, boost::any> fs; 
}; 

void baz() { 
    std::cout << "baz" << std::endl; 
} 

int main() { 
    std::function<void()> foo = []{ std::cout << "foo" << std::endl; }; 
    std::function<void(int)> bar = [](int i){ std::cout << "bar(" << i << ")" << std::endl;  
}; 

    Functions f; 
    f.add_function("foo", foo); 
    f.add_function("bar", bar); 
    f.add_function("baz", baz); 

    f.call("foo"); 
    f.call("bar", 42); 
    f.call("baz"); 
} 

기능 : 지금은 명시 적으로 모든 성병 :: 기능하고 작동하지만 그것도 대한 해결 방법이있을거야 (업데이트 무료 기능에 대한 과부하를 추가). 좋은 생각? 정확히 모든 유형 을 가져와야하므로 f.call("bar", 42u)이 실패합니다.

0

당신이 정말로 어떤 기능을 저장하려면, 항상 올바르게 호출 할 수는 Oncaphillis '접근 방식을 확장 그냥 가서 함수 포인터를 캐스팅하는 방법을 알아낼 수있는 경우 : 그럼 당신을

void foo(int); 
float bar(double, struct baz); 

std::map<void(*)()> map = { 
    {"foo", (void(*)())foo}, 
    {"bar", (void(*)())bar} 
}; 

을 당신이 그들을 사용하는 경우이를 다시 캐스트 할 수 있습니다 : 보시다시피

//code to make sure that map["foo"] is of type `void(*)(int)` 
(*(void(*)(int))map["foo"])(42); 

//code to make sure that map["bar"] is of type `float(*)(double, struct baz)` 
float result = (*(float(*)(double, struct baz))map["foo"])(3.14159, (struct baz){ /*whatever*/}); 

를, 가변 사람에 제한하지 않고, 기능 방식의 모든 유형을 호출 할 문제가 없다. 그러나이 접근법은 유형 시스템에서 제공하는 안전성을 완전히 없애고 오류가 발생하기 쉽고 캐스트가 100 % 정확해야합니다. 당신이하지 않으면 이상한 일들이 일어날 수 있습니다. 이는 boost::any을 사용할 때와 같은 문제입니다.

관련 문제