2015-01-03 2 views
0

C++의 가변 함수는 사용자가 임의의 개수의 인수로 함수를 호출 할 수있게합니다. 예를 들어, sscanf는 구문 분석 할 문자열, 형식 문자열 및 구문 분석 된 항목의 값을 취할 매개 변수 집합을 입력으로 사용합니다. 이 패러다임을 비동기로 만들고 싶다면 어떻게해야할까요? 일부 데이터를 구문 분석하여 일부 바이트에서 다양한 수의 매개 변수를 추출해야합니다. 추출해야하는 매개 변수는 sscanf와 같은 형식 문자열로 지정됩니다.C++ 가변 콜백

function <void (int, int, int)> myfunc = [=] (int a, int b, int c) 
{ 
    // Do something here! 
}; 

asyncsscanf(my_bytes, "iii", myfunc); 

asyncsscanf가 필요한 처리를해야 완료 할 때 나는 형식 문자열에 지정된 올바른 인수 myfunc에 전화를하고 싶습니다 : 내 기능은 다음과 같이 호출 할 수 싶습니다. 그런 일을 할 수 있습니까?

고맙습니다.

+0

원시 타입이있을 때 왜 ""iii "'가 필요합니까? – Yakk

+0

나는 당신의 요점을 알고 있는지 확신하지 못합니다. 더 자세히 설명해 주시겠습니까? –

+0

함수에''iii''를 전달합니다. 왜?'myfunc'는'function '유형입니다 - 왜 그 정보가 중복 된 문자열을 전달합니까? 왜 런타임 데이터를 사용합니까? – Yakk

답변

3

나는 당신의 접근 방법을 모릅니다.

먼저 asyncscanf 함수의 세 번째 매개 변수에는 좋은 유형이 없습니다. asyncscanf 본문에서만 받아 들일 수있는 것은 void (...) (인수를 무제한으로 사용하고 아무 것도 반환하지 않는 함수)이지만, asyncscanf에 전달하는 인수는 세 번째 형식으로 받아 들일 수없는 형식이어야합니다.

둘째, 형식 (예 : "iii")에 따라 my_bytes를 배포해야합니다. 유한 형식의 여러 형식 문자열이있는 경우이 작업을 수행 할 수 있습니다 (가능한 모든 형식을 '전환'할 수 있습니다). 그러나 일반적으로 나는 그것을 할 수 없다고 생각합니다.

그러나 'variadic-templates'로 질문을 표시했기 때문에 C++ 11/14를 사용하고 있다고 가정합니다. 아마도 asyncscanf의 형식 인수를 더 읽기 쉬운 템플릿 인수로 만들고 싶습니다 (형식이 컴파일 타임에 항상 알려져 있다고 가정합니다). 아래의 솔루션 요약.

  • 아이디어는 우리가 두 개의 매개 변수 팩을 가지고있다 : 코드에 대해

    #include <functional> 
    #include <iostream> 
    
    // template parsing function, remaining_bytes parameter should contain pointer to not parsed part 
    // of my_bytes 
    template <typename return_type> return_type parse(const char *my_bytes, const char *&remaining_bytes); 
    
    // specialization of parsing function for desired types, fake implementation 
    template <> int parse<int>(const char *my_bytes, const char *&remaining_bytes) { 
        remaining_bytes = my_bytes; 
        return 0; 
    } 
    
    // specialization of parsing function for desired types, fake implementation 
    template <> long parse<long>(const char *my_bytes, const char *&remaining_bytes) { 
        remaining_bytes = my_bytes; 
        return 1; 
    } 
    
    // declare helper template for general case 
    template <typename to_be_parsed_tuple, typename parsed_tuple> 
    struct asyncscanf_helper; 
    
    // all params parsed case 
    template <typename... parsed_params> 
    struct asyncscanf_helper<std::tuple<>, std::tuple<parsed_params...>> { 
        void operator()(const char *, std::function<void(parsed_params...)> fun, parsed_params... params) { 
         fun(params...); 
        } 
    }; 
    
    // some params to be parsed case 
    template <typename first_param_to_be_parsed, typename...to_be_parsed_params, typename... parsed_params> 
    struct asyncscanf_helper<std::tuple<first_param_to_be_parsed, to_be_parsed_params...>, std::tuple<parsed_params...>> { 
        void operator()(const char *my_bytes, std::function<void(parsed_params..., first_param_to_be_parsed, to_be_parsed_params...)> fun, parsed_params... params) { 
         const char *remaining_bytes = 0; 
         first_param_to_be_parsed p1 = parse<first_param_to_be_parsed>(my_bytes, remaining_bytes); 
         asyncscanf_helper<std::tuple<to_be_parsed_params...>, std::tuple<parsed_params..., first_param_to_be_parsed>>()(remaining_bytes, fun, params..., p1); 
        } 
    }; 
    
    template <typename... params> 
    void asyncscanf(const char *my_bytes, void function(params...)) { 
        asyncscanf_helper<std::tuple<params...>, std::tuple<>>()(my_bytes, function); 
    } 
    
    void test_fun(int a, int b, int c) { 
        std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl; 
    } 
    
    void test_fun2(int a, long b, int c) { 
        std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl; 
    } 
    
    int main() { 
        asyncscanf("1 2 3", test_fun); 
        asyncscanf("1 2 3", test_fun2); 
    } 
    

    노트 이미 구문 분석 매개 변수에 대해 아직 분석하지 않는 하나의 매개 변수와 일을하고 두 번째로 첫 번째 팩에서 하나 매개 변수 하나를 통과; 모든 매개 변수를 구문 분석 할 때 단순히 함수를 호출하십시오.

  • asyncscanf 컴파일러의 세 번째 인수로 전달 된 함수가 필요로하는 유형의 구문 분석 함수를 전문적으로 사용하지 않으면 컴파일러에서 알려줍니다.
  • 하나의 템플릿 함수에서 두 개의 매개 변수 팩을 사용하는 데 문제가 있으므로 asyncscanf_helper라는 간단한 함수 템플릿 대신 struct asyncscanf_helper 및 tuples 구조체를 사용해야했습니다.
  • asyncscanf_helper에서 std :: function을 사용했습니다. 왜냐하면 좀 더 일반적이고 예를 들어 사용할 수 있기 때문입니다. lambda를 인수로 사용하지만, 현재는 asyncscanf에 표준 함수를 매개 변수 유형으로 남겨 둡니다. 그렇지 않으면 두 번째 인수를 명시 적으로 std :: function으로 명시 적으로 형변환해야하기 때문에 적절한 서명 또는 템플릿 매개 변수를 명시 적으로 명시해야합니다.
  • 구문 분석 기능 전문화의 가짜 구현 때문에 코드를 실행해도 예상 결과를 볼 수 없지만 구문 분석이 질문의 일부가 아니기 때문에 나는 그것을 조롱했습니다.
+0

약간의 개선점은 'template'타입을 T로 변환 할 수있는 pointer-to-T로 대체함으로써'parse'를위한 ADL을 가능하게하는 것입니다. 거기에 쓸 수도 있고 초기화 할 수도 있고 무시할 수도 있고 값을 반환 할 수도 있습니다. – Yakk