How do I redefine a call like this via C preprocessor Instructions to snprintf?
sprintf_s<sizeof(dataFile)>(dataFile, arg2, arg3);
I tried this (which doesn't work):
#define sprintf_s<sizeof(x)>(args...) snprintf<sizeof(x)>(args)
Especially because I already need this for calls to sprintf_s without a template in the same files:
#define sprintf_s(args...) snprintf(args)
This is simply not supported by the preprocessor. The preprocessor is largely the same as the C preprocessor and C has no notion of templates.
As mkrs said in his/her answer, the preprocessor doesn't allow you to match template-like function invocations.
You don't need the preprocessor for this task - use a variadic template instead:
template <int Size, typename... Ts>
auto sprintf_s(Ts&&... xs)
{
return snprintf<Size>(std::forward<Ts>(xs)...);
}
If snprintf uses va_arg, you will need a different kind of wrapper:
template <int Size>
void sprintf_s(char* s, ...)
{
va_list args;
va_start(args, s);
snprintf(args);
va_end(args);
}
See How to wrap a function with variable length arguments? for more examples.
Related
I was wondering if there was a clean method to extract types from __VA_ARGS__ and use them to fill template method or structure definitions?
Thanks in advance
I have the following problem:
my macro definition:
#define MY_MACRO(topic, callback_name, message_type, ...) \
Callback_data<message_type, ##__VA_ARGS__> callback_name##_data(callback_name, topic); \
Handler::add_callback<message_type, ##__VA_ARGS__>(callback_name##_data)
my macro call:
MY_MACRO("/my_topic_1", callback_1, bool, int a, int b);
what I'd like my macro writes:
Callback_data<bool, int, int> callback_1_data(callback_1, "/my_topic_1");
Handler::add_callback<bool, int, int>(callback_1_data);
what my macro actually writes:
Callback_data<bool,int a, int b> callback_1_data(callback_1, "/my_topic_1");
Handler::add_callback<bool,int a, int b>(callback_1_data);
//! error on both lines, template filling expecting only type definitions !
PS : If anyone knows of a modern way to do the same thing without using macros, I'm interested too!
I'm very new to meta-programming and I'm experimenting with some examples.
I've designed a variadic template class as follows:
template <typename TA, typename... TB>
class A
[...]
This could be instantiated simply by passing different types like
A<Class1, Class2, Class3> * a = &(A<Class1, Class2, Class3>::instance());
where the pack TB only contains Class2 and Class3.
Now, what I would like to achieve is to define in my header file a list of default types and build it gradually using #ifdef definitions. I'll make an example
// concept of list of types
template<class... Ts>
struct typelist{};
// concatenating two typelists
template<class... Ts, class... Us>
auto concat(typelist<Ts...>, typelist<Us...>) -> typelist<Ts..., Us...>;
static typelist<> defaultTypelist;
#ifdef MACRO1
defaultTypelist = concat(defaultTypelist, typelist<Class2>);
// or something like defaultTypelist.push_front(Class2);
#endif
#ifdef MACRO2
defaultTypelist = concat(defaultTypelist, typelist<Class3>);
// or something like defaultTypelist.push_front(Class3);
#endif
By doing that, in my main I would like to instantiate my object as follows:
A<Class1, defaultTypelist> * a = &(A<Class1, defaultTypelist>::instance());
The main problem I see here is that I'm not able to gradually append types to defaultTypelist since it was declared in the beginning at empty template typelist<>. The compiler also returns an error if I try to pass an empty typelist as second parameter.
This may be trivial but so far I was not able to solve this, mainly because, as a beginner in meta-programming, compiler errors are not very helpful to me.
For this project, I have to use C++14 and below.
Big fat warning: don't do this.
The reason I'm saying, it'll change during the code. As you gradually add types, you'll end up defaultTypeList having multiple meanings in different places of the code.
That said... Can it be done? Of course,
#define DEFAULT_TYPE_LIST typelist<>()
// ...
#ifdef MACRO1
static constexpr auto macro1_prev_dtl = DEFAULT_TYPE_LIST;
#undef DEFAULT_TYPE_LIST
#define DEFAULT_TYPE_LIST concat(macro1_prev_dtl, typelist<Class2>())
#endif
// ...
#ifdef MACRO2
static constexpr auto macro2_prev_dtl = DEFAULT_TYPE_LIST;
#undef DEFAULT_TYPE_LIST
#define DEFAULT_TYPE_LIST concat(macro2_prev_dtl, typelist<Class3>())
#endif
... then use DEFAULT_TYPE_LIST wherever you'd like to access the current state of the list.
I have some code implemented in template variadic functions that uses modern c++17 features. Being templates, they are implemented in the .h files.
// .H FILE
template <typename... T>
inline constexpr void foo(const T& ...values){
// Do stuff
}
Is there a way to create a compatibility layer that would allow users to access this functions from C?
The way I actually solved may not be valid to all particular cases!!
I found that trying to pass arguments directly to a c++ variadic function was not possible (to the best of my knowledge). Instead, a void vector would be passed, and the results will not be the ones expected. In this case, stringifying the c input, and passing it to the c++ function worked just fine.
#ifdef __cplusplus
extern "C" {
#endif
void cfoo(const char * fmt, ...)
{
va_list args
va_start(args, fmt);
char str[1024];
vsprintf(str, fmt, args);
cpp::foo(str); // My c++ function
va_end(args);
}
#ifdef __cplusplus
}
#endif
I'm adding a macro used in C file which should take a variable number of arguments, which should be handled based on the type and the number of arguments differently. If it's pure C++, it's easily achieved by overloading the function, but how would I pass the variadic arguments from C macro into C++ with this mixture of C and C++?
The C file is restricted to be compiled with gcc. In macro definition, I passed the variadic arguments into C wrapper function. Since the number of arguments is unknown, I have a macro counting the arguments and pass it along into va_list. But using this approach, I do not know the type of arguments to flexibly pass any arguments to C++ function. I'm including relevant code snippet reflecting the current structure of code, skipping the actual handling logic in cpp file and other irrelevant information.
In use.c:
#include "macro.h"
LOG(id, lvl, params);
In macro.h:
#define LOG(_MSG_ID_, _LOG_LVL_, ...) \
log_data(&hdr, ##__VA_ARGS__); \
In logger.h:
#define GET_NARG(_1, _2, _3, _4, N, ...) N
#define COUNT_VARARGS(...) GET_NARG(__VA_ARGS__, 4, 3, 2, 1)
#define log_data(p_hdr, ...) \
log_data_c(p_hdr, COUNT_VARARGS(__VA_ARGS__), ##__VA_ARGS__);
#ifdef __cplusplus
class LOGGER
{
public:
void log_data(LOG_HEADER_s* hdr);
void log_data(LOG_HEADER_s* hdr, uint16_t val);
void log_data(LOG_HEADER_s* hdr, uint64_t val);
void log_data(LOG_HEADER_s* hdr, uint32_t val1, uint32_t val1);
// and other overloaded functions
static inline LOGGER& getInstance() { return m_instance; }
private:
static LOGGER m_instance;
};
#else
typedef struct LOGGER LOGGER;
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern void log_data_c(LOG_HEADER_s* hdr, int n, ...);
#ifdef __cplusplus
}
#endif
In logger.cpp:
#include "logger.h"
#include <stdarg.h>
LOGGER LOGGER::m_instance = LOGGER();
#define LOGGER_Instance LOGGER::getInstance()
#ifdef __cplusplus
extern "C" {
#endif
void log_cmn_data(LOG_HEADER_s* hdr, int n, ...)
{
va_list args;
va_start(args, n);
LOGGER_Instance.log_data(va_arg(args, LOG_HEADER_s*));
va_end(args);
}
#ifdef __cplusplus
}
#endif
The ideal scenario would be just passing the variadic arguments in the macro into the invocation of C++ overloaded function. Any workarounds to achieve the result are welcome. I've been trying to get this working for a while but I haven't found a post dealing with the same scenario. Any help is appreciated.
Since the number of arguments is unknown, I have a macro counting the
arguments and pass it along into va_list. But using this approach, I
do not know the type of arguments to flexibly pass any arguments to
C++ function.
That's right, you don't know. The C mechanism for variadic functions does not directly provide the called function any information about the number or types of the variable arguments. The called function must use a combination of assumptions and information gleaned from its arguments to make that determination. The printf function is the canonical example: it determines both the number of variable arguments and their types by analyzing the the provided format string (and havoc ensues if the arguments actually provided are mismatched with the format).
Provided that you place a fixed, artificial upper limit on the number of variable arguments supported, you can indeed count them via a variadic macro, as demonstrated. Very little type information is available to the preprocessor, however, and there is no applicable mechanism for applying what little such information there is to your purpose.
The usual C alternative to function overloading is simply to write functions with different names. If you have similar functions that differ primarily in parameter number and type, then you might give them related names that convey those types. For example,
void log_data(LOG_HEADER_s *);
void log_data_u16(LOG_HEADER_s* hdr, uint16_t);
void log_data_u64(LOG_HEADER_s* hdr, uint64_t);
void log_data_u32_u32(LOG_HEADER_s* hdr, uint32_t, uint32_t);
Alternatively, it might be more appropriate to give them names that convey the purpose of their particular signature. I'm inclined to suspect that an approach along these lines would work better for you than trying to multiplex your several distinct logging functions through a single variadic interface.
On the other hand, if you insist on providing a single variadic function interface, then you could put multiple for-purpose macros in front of it on the C side, each corresponding to one of the supported back-end signatures. These would not need to be variadic (unless the corresponding specific function was). This would be to your advantage because you would get at least argument-count validation from your compiler (for calls going through the macros), and they could provide whatever extra arguments are needed to convey the expected number and types of arguments to the variadic interface function.
We've got a bunch of legacy code that doesn't support Unicode, so a transitional pattern we use in our code is to move the function to a .inl file, change char to CHAR_TYPE, and then wrap it up like this:
#define CHAR_TYPE wchar_t
#define STRING_TYPE std::wstring
#define MyFunctionName MyFunctionNameW
#include "MyFunction.inl"
#undef CHAR_TYPE
#define CHAR_TYPE char
#undef STRING_TYPE
#define STRING_TYPE std::string
#undef MyFunctionName
#define MyFunctionName MyFunctionNameA
#include "MyFunction.inl"
...where MyFunction.inl then defines MyFunctionName, using the macros to generate both an 'A' version and a 'W' version.
This is icky, but it's unfortunately necessary until we get all of our code converted to support Unicode.
Is there an alternative way I could do this with templates? I'm thinking that something like the following would be nice:
typedef MyFunctionName<wchar_t, std::wstring> MyFunctionNameW
typedef MyFunctionName<char, std::string> MyFunctionNameA
Is this possible?
Update: I misread the question, thinking you already had A and W functions written which you wanted to call through the same name. What you have is indeed a situation for which templates are designed, but I won't repeat the correct answer.
Simple overloading works, no need for templates or additional complexity. The Win32 API uses macros because C doesn't have overloading.
inline
void MyFunctionName(std::wstring const& value) { MyFunctionNameW(value); }
inline
void MyFunctionName(std::string const& value) { MyFUnctionNameA(value); }
Roger Pate is entire correct about the interface. You shouldn't bother with A and W suffixes. However, this still leaves the problem of implementation. As you supected, templates are the correct solution. And since you don't need the different names, you can leave out the typedefs. You would just have
template <typename TSTRING> void MyFunctionName (TSTRING const&);
yes its possible, this is basically what std::string and std::wstring do anyway as they are actually typedefs of std::basic_string<charT,traits,alloc>.