c ++ передача верхнего диапазона последовательности в шаблон функции с переменными числами

Я вызываю API особым образом с помощью шаблонов, и у меня осталась одна проблема с передачей постоянного параметра.

Моя попытка с в привязке:

    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 является специализацией для API с 3 аргументами, потому что я до сих пор не могу передать верхнюю границу — 4 в данном случае — для генерации последовательности: <1,2,3>.
Эта последовательность необходима для вызова API с 3 аргументами, где tupel начинается с параметра rval в f3 ().

Фон:

API это #define

F3 вызывает API.

f3 обрабатывает возвращаемое значение API в позиции 0 последовательности / tupel.

f3 вызывает со всеми параметрами другую переменную функцию для записи отладочной информации.

Один tupel и две последовательности для двух вызовов функций.

ПРОБЛЕМА:

Я хочу передать параметр для управления верхней границей последовательности, не заданной размером tupel, а сигнатурой функции API.

Мне нужен только один fn () для всех API, а не f0 (), f1 (), f2 (), f3 () ….. для API с 0, 1, 2, 3 … аргументами.

Я хочу что-то вроде этого:

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

Заранее спасибо.

Постскриптум
Я использую Microsoft Visual Studio 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

Решение

Не совсем понятно, как вы хотите обрабатывать ошибки и т. Д. Я предполагал вернуть кортеж с кодом ошибки и значением.

Вот общий шаблон, который, я думаю, сделает то, что вы хотите. Вы должны быть осторожны в отношении специализаций и перегрузок emit_logособенно с байтовыми массивами, которые не могут заканчиваться нулем или содержать непечатные символы.

Я использовал узкие символы для удобства, но эта идея будет работать с широкими символами с несколькими изменениями.

Примечание: отредактировано на 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

Как получить количество аргументов в вызове 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);
}
};
1

Другие решения

Благодаря Ричарду Ходжесу я смог решить мою проблему:

Теперь для каждого 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;
}
0

По вопросам рекламы [email protected]