我在模板的帮助下以特定方式调用 API,但在传递常量参数时留下了一个问题。
我对 int bound 的尝试:
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, path, MAX_PATH, L"EXE-path");
f3 是具有 3 个参数的 API 的专门化,因为我到现在为止无法传递上限 - 在本例中为 4 - 以生成序列:<1,2,3>。 需要此序列来调用具有 3 个参数的 API,其中元组从 f3() 中的参数 rval 开始。
背景:
api 是一个#define
f3 调用 API。
f3 在sequence/tupel的0位置处理API的返回值。
f3 使用所有参数调用另一个可变参数函数来记录调试信息。
两个函数调用的一个元组和两个序列。
问题:
我想传递一个参数来控制不是由 tupel-size 而是由 API 函数签名给出的序列的上限。
我只想要一个 fn() 用于所有 API,而不是 f0()、f1()、f2()、f3() ..... 用于具有 0、1、2、3 ... 参数的 API。
我想要这样的东西:
err = fn(seq3<4>, api(GetModuleFileName), rval, nullptr, path, MAX_PATH, L"EXE-path")
这是我的工作代码:
#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; }
提前致谢。
附言 我正在使用 Microsoft Visual Studio 2015
更新:
我尝试按照 Richard Hodges 解决方案中的模板 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>
调用站点.nofArg() = 3
How to get the correct number of arguments?
最佳答案
还不完全清楚您想如何处理错误等。我假设返回错误代码和值的元组。
这是一个通用模式,我认为它可以满足您的需求。您需要注意 emit_log
的特化和重载,尤其是对于可能不是 null 终止或包含非打印字符的字节数组。
为了方便起见,我使用了窄字符,但这个想法只需进行一些编辑就可以用于宽字符。
注意:在 linux 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:120@main - calling GetModuleFileName(nullptr,,255) -> returns: { error: 0, value: 14 }
http://coliru.stacked-crooked.com/a/e5da55af212d5500
How do I get the number of arguments in the API call?
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);
}
};
关于c++将序列上限传递给可变函数模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46503560/