2017-09-30 2 views
-1

템플릿을 사용하여 특정 방식으로 API를 호출하고 있으며 상수 매개 변수를 전달하는 데 한 가지 문제가 있습니다.C++ 시퀀스의 상위 범위를 가변 함수 템플릿

INT 바운드 나의 시도 : 주에서

template <typename F, typename ...Ts> 
      static int f3(int bound, CString file, int line, CString Caller, 
         CString f_name, F f, Ts&& ...ts) { 
       int err = fn(bound, file, line, Caller, f_name, 
       f, std::tuple<Ts...>(ts...), seq3<bound>{}, // error C2975 
       seq1<sizeof...(Ts)>{});     
       return err; 
    } 

:

int const bound; 
bound = 4; 

err = fn(bound, api(GetModuleFileName), rval, nullptr, path, MAX_PATH, L"EXE-path"); 

compiler error C2975: 'N': invalid template argument for 'seq3', expected compile-time constant expression 

어떻게이 문제를 해결하려면? 지금 쯤

내 해결책 :

ERR = F3 (API (하여 GetModuleFileName) rval에, nullptr, 경로 MAX_PATH, L "EXE 경로");

f3은 최대 인수 (이 경우 4)를 전달할 수 없으므로 3 개의 인수가있는 API의 특수화입니다. < 1,2,3>. 이 시퀀스는 tupel이 f3()의 매개 변수 rval에서 시작하는 3 개의 인수로 API를 호출하는 데 필요합니다.

배경 :

API를 정의 #

F3는 API를 호출합니다.

f3은 시퀀스/튜플의 0 위치에서 API의 반환 값을 처리합니다.

f3은 모든 매개 변수로 디버그 정보를 기록하는 또 다른 가변 함수를 호출합니다.

두 개의 함수 호출에 대해 하나의 투플과 두 개의 시퀀스.

문제 :

나는 tupel 크기가 아니라 API 함수 서명에 의해 주어지지 시퀀스의 상한을 제어하는 ​​매개 변수를 전달하고 싶다.

0, 1, 2, 3 ... 인수가있는 API의 경우 f0(), f1(), f2(), f3() .....이 아닌 모든 API에 대해 하나의 fn .

나는 이런 식으로 뭔가 싶어 :

ERR = FN (seq3 < 4>, API (하여 GetModuleFileName), rval에, nullptr, 경로, MAX_PATH는, L "EXE 경로")는

여기 내 작업의 코드 :

#include <windows.h> 
#include <atlstr.h> 
#include <tuple> 
#include <utility> 

template <int ... Ns> struct seq_3 {};         
template <int ... Ns> struct seq3_n {}; 

template <int I, int ... Ns> struct seq3_n<I, Ns...>{ 
    using type = typename seq3_n<I - 1, I - 1, Ns...>::type;}; 

template <int ... Ns> struct seq3_n<1, Ns...>{ 
// skip first argument : rval, because it doesn't fit to API, 
// but needed for calling other function  
    using type = seq_3<Ns...>;    }; 

template <int N> 
    using seq3 = typename seq3_n<N>::type; 

template <int ... Ms> struct seq_1 {};       
template <int ... Ms> struct seq1_n {};  
template <int J, int ... Ms> struct seq1_n<J, Ms...>{ 
    using type = typename seq1_n<J - 1, J - 1, Ms...>::type; };  
template <int ... Ms> struct seq1_n<0, Ms...> { 
    using type = seq_1<Ms...>;    }; 
template <int M> 
    using seq1 = typename seq1_n<M>::type;  

template <typename F, typename TUP, int ... INDICES3, int ... INDICES1>       
    static int fn(CString file, int line, CString Caller, CString f_name, 
       F f, TUP tup, seq_3<INDICES3...>, seq_1<INDICES1...>) { 
       int err = 0; 
       // handling of rval = first element of tuple 
       std::get<0>(tup) = f(std::get<INDICES3>(tup) ...); // calling API 
       err = GetLastError(); 
       /* calling next function (variadic too) with same tupel, but other sequence 
       myOpenDebugOutputString(project, file, line, Caller, f_name, std::get<INDICES1>(tup) ..., "stop"); 
       */ 
       return err; } 

template <typename F, typename ...Ts> 
    static int f3(CString file, int line, CString Caller, CString f_name, 
       F f, Ts&& ...ts) { 
       int err = fn(file, line, Caller, f_name, 
       f, std::tuple<Ts...>(ts...), seq3<4>{}, // sequence fixed by f3 
       seq1<sizeof...(Ts)>{});     // 3 arguments api + skip 1 rval = 4 
       return err;        // given by signature of API 
} 


int main() {  
    // for calling simple API GetModulFileName with 3 arguments  
    //          returns len(path) 
    wchar_t  path[MAX_PATH];  
    DWORD   rval = 0; 
    int   err = 0; 
    rval = GetModuleFileName(nullptr, path, MAX_PATH);  
    err = GetLastError(); 

#define api(a) __FILE__, __LINE__, __func__, L#a, a 
// L#a becomes L"GetModuleFileName" 

    err = f3(api(GetModuleFileName), rval, nullptr, path, MAX_PATH, L"EXE-path"); 

    return 0; } 

미리 감사드립니다.

P. 나는 마이크로 소프트 비주얼 스튜디오에게 2015

업데이트를 사용하고 있습니다 : 나는 리처드 스 솔루션에서 템플릿 api_call에 다음과 같은 노력

합니다.

std::tuple<GivenArgs...> tup(args...); 

// OK, but only for an api with 3 arguments 
callsite.function(std::get<0>(tup), std::get<1>(tup), std::get<2>(tup)); 

// compiler error too many arguments 
callsite.function(std::forward<GivenArgs>(args)..., seq1<callsite.nofArgs()>{}); 

// compiler error too few arguments 
callsite.function(tup, seq1<callsite.nofArgs()>{}); 

비고 :

seq1 < 3> = seq_1 < 0,1,2>

callsite.nofArg() =의 정확한 수를 얻을 수있는 방법

3

논쟁?

+1

실제로 무엇을하려고합니까? 콜 사이트에 대한 정보를 포함하여 API에 호출을 기록 하시겠습니까? –

+0

^그. 나는이 질문을 이해하지 못한다. 그러나 만약 당신이 에러 메시지를 가지고 있다면 그것들을 [편집]한다. –

+0

네, 포맷 된 방식으로 호출 사이트 정보와 많은 다른 변수를 기록하려고합니다. – CarpeDiemKopi

답변

1

오류 등을 처리하는 방법이 완전히 명확하지 않습니다. 오류 코드와 값의 튜플을 반환하는 것으로 가정했습니다.

다음은 내가 원하는대로 할 수있는 일반적인 패턴입니다. emit_log의 특수화 및 과부하, 특히 널 (null)로 끝나거나 인쇄 할 수없는 문자를 포함 할 수없는 바이트 배열의 경우주의해야합니다.

편의를 위해 좁은 문자를 사용했지만이 아이디어는 몇 가지 수정 사항이있는 넓은 문자와 함께 사용할 수 있습니다.

참고 : gcc에서 편집하여 Windows API를 시뮬레이션했습니다.

#include <cstdint> 
#include <utility> 
#include <iostream> 
#include <variant> 

#define WINAPI 
#define _In_opt_ 
#define _Out_ 
#define _In_ 

struct _hmodule {}; 
using HMODULE = _hmodule*; 
using LPTSTR = char*; 
using LPCTSTR = const char*; 
using DWORD = std::uint32_t; 

extern DWORD WINAPI GetModuleFileName(
    _In_opt_ HMODULE hModule, 
    _Out_ LPTSTR lpFilename, 
    _In_  DWORD nSize 
); 

extern WINAPI DWORD GetLastError(); 

template<class Ret, class...Args> 
struct api_call_site 
{ 
    const char* file; 
    int line; 
    const char* current_function; 
    const char* called_function; 
    Ret (* function)(Args...); 
}; 

template<class Ret, class...Args> 
auto make_api_call_site(const char* file, int line, const char* callername, const char* calleename, Ret (* WINAPI callee)(Args...)) 
{ 
    return api_call_site<Ret, Args...> 
    { 
     file, 
     line, 
     callername, 
     calleename, 
     callee 
    }; 
} 

template<class T> 
void emit_log(LPCTSTR& sep, std::ostream& os, T&& x) 
{ 
    os << sep << x; 
    sep = ","; 
} 

template<class Ret> 
struct error_with_value 
{ 
    DWORD error; 
    Ret value; 

    bool has_error() const { return error != 0; } 
    friend std::ostream& operator<<(std::ostream& os, const error_with_value& ewv) 
    { 
     os << "{ error: " << ewv.error << ", value: "; 
     LPCTSTR sep = ""; 
     emit_log(sep, os, ewv.value); 
     os << " }"; 
     return os; 
    } 
}; 


#define api(a) make_api_call_site(__FILE__, __LINE__, __func__, #a, a) 


// this will need some specialisations... 
void emit_log(LPCTSTR& sep, std::ostream& os, std::nullptr_t) 
{ 
    os << sep << "nullptr"; 
    sep = ","; 
} 

template<class Ret, class...Args, class...GivenArgs> 
auto api_call(api_call_site<Ret, Args...> const& callsite, GivenArgs&&...args) -> error_with_value<Ret> 
{ 
    // log call here 
    std::clog << callsite.file << ":" << callsite.line << "@" << callsite.current_function << " - "; 
    std::clog << "calling " << callsite.called_function << "("; 
    // appropriate code to print arguments in a safe way here... 
    LPCTSTR sep = ""; 
    using expand = int[]; 
    void(expand{0, 
     (emit_log(sep, std::clog, args),0)... 
    }); 
    std::clog << ")"; 
    error_with_value<Ret> result 
    { 
     0, 
     callsite.function(std::forward<GivenArgs>(args)...) 
    }; 
    result.error = GetLastError(); 

    std::clog << " -> returns: " << result; 
    return result; 
} 

int main() 
{ 
    char buffer[255]; 
    DWORD bufsize = 255; 

    auto result = api_call(api(GetModuleFileName), nullptr, buffer, bufsize); 
    if (! result.has_error()) 
    { 
     // 
    } 

} 

예 출력 :

main.cpp:[email protected] - calling GetModuleFileName(nullptr,,255) -> returns: { error: 0, value: 14 } 

http://coliru.stacked-crooked.com/a/e5da55af212d5500

가 어떻게 API 호출에 인수의 수를받을 수 있나요? 나는 내 문제를 해결할 수 리처드 스에

template<class Ret, class...Args> 
struct api_call_site 
{ 
    const char* file; 
    int line; 
    const char* current_function; 
    const char* called_function; 
    Ret (* function)(Args...); 

    // like this 
    static constexpr std::size_t nofArgs() 
    { 
     return sizeof...(Args); 
    } 
}; 
+0

고마워요. 넓은 문자로 변경 한 원래 Windows API GetModuleFilename 사용하려고 및 컴파일러 오류가 있습니다 : 오류 C2672 : 'make_api_call_site': 일치하는 오버로드 된 함수 오류 C2784 발견 : '자동 char_, int, const char * const char *, Ret (__cdecl *) (Args ...)) ':'DWORD (HMODULE, LPWSTR, DWORD) '에서'Ret (__cdecl *) (Args ...) '에 대한 템플릿 인수를 추론 할 수 없습니다! 이 문제를 해결하는 방법? – CarpeDiemKopi

+0

@CarpeDiemKopi 로깅 기능 등에서 문자열의 너비를 확인해야합니다. –

+0

컴파일러 오류를 제거했습니다. 문제는 WINAPI의 사용이었습니다. Ret (* function) (Args ...)를 변경했습니다. Ret (WINAPI * 함수) (Args ...); \t 및 Ret (* WINAPI callee) (Args ...))를 Ret (WINAPI * callee) (Args ...))로 변경합니다. 이 hast는 Windows API 시뮬레이션에 WINAPI를 정의하기 때문에 이전에 작동했습니다. – CarpeDiemKopi

0

감사 :

지금 모든 API에 대한 하나의 api_call 거기에 그 api_call 뒤에 템플릿을 호출 API의 서명을 결정합니다. 내가 요구했던 것보다 훨씬 낫다.

#include <windows.h> 
#include <atlstr.h> 
#include <tuple> 

template <int ... Ns> struct seq_3 {}; 
template <int ... Ns> struct seq3_n {}; 
template <int I, int ... Ns> struct seq3_n<I, Ns...> { 
    using type = typename seq3_n<I - 1, I - 1, Ns...>::type; 
}; 
template <int ... Ns> struct seq3_n<1, Ns...> { 
    // this sequence is more complicated in my real code, because 
    // there are more variables for logging, but not for api calling  
    using type = seq_3<Ns...>; 
}; 
template <int N> 
using seq3 = typename seq3_n<N>::type; 

template <int ... Ms> struct seq_1 {}; 
template <int ... Ms> struct seq1_n {}; 
template <int J, int ... Ms> struct seq1_n<J, Ms...> { 
    using type = typename seq1_n<J - 1, J - 1, Ms...>::type; 
}; 
template <int ... Ms> struct seq1_n<0, Ms...> { 
    using type = seq_1<Ms...>; 
}; 
template <int M> 
using seq1 = typename seq1_n<M>::type; 

// according to the solution from Richard Hodges 
// ********************************************* 
template<typename Ret, typename...Args> 
struct api_call_site 
{ 
    const CString file; 
    int line; 
    const CString Caller; 
    const CString f_name; 
    Ret(WINAPI* function)(Args...);  

    static constexpr std::size_t nofArgs() { 
     return sizeof...(Args); 
    } 
}; 

template<typename Ret, typename...Args> 
auto make_api_call_site(const CString file, int line, const CString Caller, const CString f_name, Ret(WINAPI* callee)(Args...)) 
// WINAPI see also here https://stackoverflow.com/questions/18912931/why-need-to-use-winapi-for-the-syntax-for-declaring-function-pointers-for-fun 
{ 
    return api_call_site<Ret, Args...> 
    { 
     file, 
      line, 
      Caller, 
      f_name, 
      callee 
    }; 
} 

template <typename Ret, typename...Args, typename TUP, int...INDICES3, int...INDICES1> 
int fn(api_call_site<Ret, Args...> const& callsite, TUP tup, seq_3<INDICES3...>, seq_1<INDICES1...>) { 
    int err = 0; 
    // handling of return value from api call goes always in position 0 from tuple 
    std::get<0>(tup) = callsite.function(std::get<INDICES3>(tup) ...); 
    err = GetLastError(); 
    /* calling next function (variadic too) with same tupel, but other sequence 
    myOpenDebugOutputString(project, file, line, Caller, f_name, std::get<INDICES1>(tup) ..., "stop"); 
    */ 
    return err; 
} 

template<typename Ret, typename...Args, typename...GivenArgs> 
int api_call(api_call_site<Ret, Args...> const& callsite, GivenArgs&&...args) 
{ 
    int err; 
    err = fn(callsite, std::tuple<GivenArgs...>(args...), seq3 <callsite.nofArgs()+1> {}, seq1 <sizeof...(GivenArgs)> {}); 
    return err; 
} 

int main() { 
    DWORD  size_path = 20; // make it small and get error 122 
    wchar_t  path[MAX_PATH]; // ERROR_INSUFFICIENT_BUFFER 
    DWORD  rval = 0; 
    int   err = 0; 
    CString  tolog1(L"EXE-Path determined"); 
    int   tolog2 = 25; 
    // old way without logging information 
    rval = GetModuleFileName(nullptr, path, MAX_PATH); 
    err = GetLastError(); 

    // new way with logging any variables ... behind the must variables for the api 
    // **************************************************************************** 
#define api(a) make_api_call_site(__FILE__, __LINE__, __func__, L#a, a) 
    err = api_call(api(GetModuleFileName), rval, nullptr, path, size_path, tolog1, tolog2); 

    return 0; 
} 
관련 문제