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
Related
The following is some background as to what I want to do and why. The actual question is at the very bottom...
I have an API that has some format.
For instance
int f(void *str1, void *str2) {...}
I want to reimplement the system behind the API so that it can be a drop in replacement for the old system. However it turns out that str1 is now unnecessary and moreover doesn't make sense to have if you're aware that you're using my new system. Therefore, I want to be able to expose the underlying API that makes sense:
int f_internal(void *str2);
Right now I have code that looks like this:
#ifdef USE_INTERNAL
#define INTERNAL(sym) sym##_internal
#else
#define INTERNAL(sym) sym
#endif
extern "C" {
#ifndef USE_INTERNAL
int f(void *str1, void *str2){
return INTERNAL(f)(str2);
}
#endif
SOME_ATTRIBUTES
int
INTERNAL(f)(void *str2){
... // actual content
} EXPORT_FUNCTION_MACRO(INTERNAL(f), 1);
}
The effect is if I define USE_INTERNAL, a call to f(a) works, but I don't define it then we have to use the f(a, b).
The problem I am encountering is that EXPORT_FUNCTION_MACRO itself defines another function name but doesn't evaluate INTERNAL(F) first. This results in the message
dir: error: pasting ")" and "_" does not give a valid preprocessing token
INTERNAL(sym) \
--- NOTE--- EXPORT_FUNCTION_MACRO takes args 'sys' and 'n'
other_dir: note: in definition of macro ‘EXPORT_FUNCTION_MACRO’
void _example##sym##_##n(void) {} \
WHAT I WANT TO DO:
I want to have a compile flag that can change the number of arguments needed to call something.
Maybe something like (if it existed)
using f(a, b) = f_internal(a);
Any ideas?
This results in the message
To fix the message let macro arguments expand before concatenating them.
#define EXPORT_FUNCTION_MACRO_X(sys, n) EXPORT_FUNCTION_MACRO(sys, n)
EXPORT_FUNCTION_MACRO_X(INTERNAL(f), 1);
You could achieve that with a macro in the C part:
#define f(...) CONCAT(f, NUM(__VA_ARGS__, 2, 1)) (__VA_ARGS__)
#define CONCAT(X, Y) CC(X, Y)
#define CC(X, Y) X ## Y
#define NUM(X, Y, N, ...) N
#ifdef __cplusplus
extern "C"
#endif
void f_internal(void* str);
// specifically compiled for C or C++ anyway (and most likely inlined)
// -> no need for extern "C" for these:
void f1(void* str) { f_internal(str); }
void f2(void* unused, void* str) { f_internal(str); }
The macro f would select the correct function out of f1 and f2, which again would call f_internal with the correct argument. Works for both C and C++. If you prefer, you could still just provide two overloads for C++ separately and let only the C people deal with the macros.
I doubt one could call that 'elegant'; if you want to qualify as 'ugly' – up to you. But at least it works...
The following code behaves like this:
If USE_INTERNAL is defined, defines an inline (c++) / static (c) function int f(void* str).
Otherwise, defines an inline (c++) / static (c) function int f(void* str1, void* str2).
Both functions are trampolines to the actual (int f_internal(void* str)) function.
Note that since the functions are defined in the header, inline (c++) / static (c) is required to keep them from violating the ODR rule (I am a c++ person, so I don't know any way better than static to achieve this in c. If there is, please let me know).
#ifdef __cplusplus
extern "C"
#endif
int f_internal(void* str);
#ifdef USE_INTERNAL
#ifdef __cplusplus
inline
#else
static
#endif
int f(void* str1, void* str2) {
return f_internal(str2);
}
#else
#ifdef __cplusplus
inline
#else
static
#endif
int f(void* str) {
return f_internal(str);
}
#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.
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.
For some reason, when I declare variables and functions with va_list types in them, the Clang AST treats them as __va_list_tag internally. This is problematic since the latter is a builtin definition that isn't included in stdarg.h. Is there a way to tell Clang not to replace this?
Here's some example code:
#include <cstdarg>
typedef int (*process_args)(va_list args);
void useless_func() {
process_args useless_fp;
}
When I replace the types of declarations with their canonical types, I get the following:
#include <cstdarg>
typedef int (*process_args)(va_list args);
void useless_func() {
int (*useless_fp)(__va_list_tag *);
}
I'd ideally like the __va_list_tag * part to be replaced with va_list so the resulting code can be compiled as C++ code.
sprintf is an API provided by platform. I want to filter some format when it is used. My idea is:
#include <stdio.h>
int my_sprintf(...)
{
my_filter_function(...);
return ::sprintf(...);
}
#define sprintf my_sprintf
Then put these code in pch.
But I am still worrying it can't cover all usages, some one is in prebuilt library and not every project has a pch. Do you have any other idea?
Thanks. It's on windows.
You can't "overwrite" a built-in function. Furthermore, using a macro to replace its name results in your program having undefined behaviour.
So, don't even try to change the behaviour of the standard library. Really, that way madness lies.
Just call my_sprintf from your own code and let the platform do what it always did.
You want to use variadic functions.
Example:
int my_sprintf(char *buffer, char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt);
/* insert your filter here */
/* you CAN NOT re-use a va_list variable after being used */
ret = vsprintf(buffer, fmt, args);
va_end(args);
return ret;
}
Note: You are not allowed to define a function / macro with the same name as a function from the standard library. It's undefined behaviour.
You have to replace all your calls to sprintf with your custom my_sprintf function.
You can use namespace concept to define functions with the same names
#include <stdio.h>
namespace myns
{
int sprintf(...)
{
my_filter_function(...);
return ::vsprintf(...);
}
}
than call
char buffer[256];
myns::sprintf(buffer, "Hello, %s!\n", "World");