How to overload a function taking variable number of arguments - c++

I need to distinguish (overload) between two functions - one takes a single const char* argument and the other takes at least two arguments - a const char* followed by one or more arguments.
i.e. basically:
void f(const char *str);
void f(const char *format, ...)
I want the first version to be called for f("hello") and the second version for f("hello %d", 10). The above overload won't work because the compiler finds f("hello") ambiguous.
So I tried this:
void f(const char *str);
template<typename T>
void f(const char *str, T tt, ...);
This makes the overload resolution work correctly. But I end up with another problem. The second function is supposed to forward the arguments for printf-style usage. So I have something like:
template <typename T>
void f ( const char *format, T tt, ... )
{
(T)tt;
va_list varlist;
va_start(varlist, format);
vprintf(format, varlist);
va_end(varlist);
}
Now the second argument tt is no longer part of the variable argument list and calling va_start() with format does not seem to work.
Is there any way to achieve what I want?

If you use a vararg template you can accomplish what you want:
#include <iostream>
void f(const char* str)
{
std::cout << "single arg: " << str << "\n";
}
template <typename ...T>
void f(const char *format, T... args)
{
printf(format, args...);
}
int main()
{
f("hello");
f("formatted %d", 10);
}

Maybe using templates is not the best idea, since the second error seems more difficult to fix than the first. The overload of the functions is fine, it is more if you try in a separate file you will see that it works.
#include <iostream>
bool f(const char* str) {
return true;
}
bool f(const char* str,int n) {
return false;
}
int main(int argc,char* argv[]) {
std::cout << f("Hi") << std::endl; //Gives 1
std::cout << f("Hi",10) << std::endl; //Gives 0
return 0;
}
You could also try creating a namespace and putting the functions in there, thus making sure that no other files share that function name that can cause that error.

Q: why compile error?
A: becauz scope of "void f(const char *format, ...)" had cover "void f(const char *str);".
so the cute solution is you should better using arg num to distinguish 2 func.
like this:
void func_with_one_arg(const char *str) {
printf("func_with_one_arg");
}
void f(int arg_num, const char *format, ...) {
if (arg_num==1) return func_with_one_arg(format);
printf("func_with_multi_arg\n");
va_list ap;
va_start(ap, format);
va_end(ap);
}
if you want to automately coumpute variable arg_num, use this macro to help computing.
#define VA_LENGTH_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
#define VA_LENGTH(...) VA_LENGTH_(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define ExecVF(Func, ...) Func(VA_LENGTH(__VA_ARGS__), __VA_ARGS__)
now you should use ExecVF like this:
int
main(int argc, char **argv) {
ExecVF(f, "foo", "bar");
ExecVF(f, "foo");
return 0;
}

Related

How to forward the macro args to format func?

#define LOG_INFO(str, ...) \
logger.NewLogStateMent(__FILE__, __LINE__, \
fver::base::log::Logger::LogLevel::kInfo, str, \
##__VA_ARGS__)
void NewLogStateMent(const char* filename, const int len, LogLevel lev, ...) {
std::cout << fmt::format("{} {} {} {} {}", filename, lne, lev, ...);
}
// use case
int main () {
LOG_INFO("hello, world %d", 1);
}
Now i want to this LOG_INFO(str, ...) to a fmt::format();
But the Gcc give me a lot of errors
gcc version: 12.2.0
c++ version: c++17
How can i finish it?
Please help me!
Something along these lines:
#define LOG_INFO(str, ...) \
NewLogStateMent(__FILE__, __LINE__, \
"info", str, \
##__VA_ARGS__)
template <typename... Args>
void NewLogStateMent(const char* filename, const int line, const char* level,
const std::string& format_str, Args&&... args) {
std::cout << fmt::format("{} {} {} ", filename, line, level)
<< fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
}
// use case
int main () {
LOG_INFO("hello {} world", 1);
}
Demo
Here's how to do it with compile-time checks (https://godbolt.org/z/EE1zxcdrK):
#include <fmt/core.h>
#define LOG_INFO(str, ...) \
NewLogStatement(__FILE__, __LINE__, "info", str, ##__VA_ARGS__)
template <typename... T>
void NewLogStatement(const char* filename, int line, const char* level,
fmt::format_string<T...> fmt, T&&... args) {
fmt::print("{} {} {} ", filename, line, level);
fmt::print(fmt, std::forward<T>(args)...);
}
int main () {
LOG_INFO("hello {} world", 1);
}

Incorrect function call with variable parameters

I am trying to make a nested function call with a variable number of parameters without retrieving them. And I get the wrong result.
Here is my simple c++ program:
extern "C" {
#include <stdio.h>
}
#include <cstdarg>
class ctty {
public:
ctty();
int logger(int prior, const char* format, ...);
private:
};
ctty::ctty(){};
int ctty::logger(int prior, const char* format, ...)
{
va_list ap;
va_start(ap,format);
printf(format, ap);
va_end(ap);
return 0;
}
int main(int argc, char** argv)
{
ctty tty;
tty.logger(0, "Test %d %d %d\n", 7, 5, 5);
return 0;
}
result:
Test -205200 7 5
I expect a result
Test 7 5 5
I don’t understand what am I doing wrong?
Thanks in advance.
You can't directly pass va_list to printf. va_list is a wrapper around the actual list of arguments (whatever its representation is). Although C way to do should be to use vprintf, in C++ there are safer alternatives, like variadic templates allowing to create a safer version of formatted printing, e.g. (a fictional format string for brevity of example):
#include <iostream>
#include <cstdlib>
class ctty {
public:
ctty();
template<typename T, typename... Args>
int logger(int prior, const char* format, T value, Args... args);
private:
void logger(int prior, const char *s);
};
ctty::ctty(){};
void ctty::logger(int prior, const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
int ctty::logger(int prior, const char* format, T value, Args... args)
{
while (*format) {
if (*format == '%') {
std::cout << value;
logger(prior, format + 1, args...);
return 0;
}
std::cout << *format++;
}
throw std::logic_error("extra arguments provided to logger");
}
int main(int argc, char** argv)
{
ctty tty;
tty.logger(0, "Test % % %\n", 7.55f, "Tada!", 888);
return 0;
}
This part of your code:
extern "C" {
#include <stdio.h>
}
Is technically an undefined behavior, while it may compile and not have adverse effect in some cases, it's not portable. You have to use C++ headers, e.g. <cstdio>

Variadic Macro: cannot pass objects of non-trivially-copyable type through '...'

I am trying to write a macro for logging mechanism. I wrote a variadic macro but it does not work with std::string. The code looks like the following:
#include <stdio.h>
#include <string>
#define LOG_NOTE(m, ...) printf(m, ##__VA_ARGS__)
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s", "Hello World", bar, foo);
return 0;
}
If I would call the macro like following, I would not get any error.
LOG_NOTE("%s %d %s", "Hello World", bar, "random string");
Compiler Output:
In function 'int main()': 5:49: error: cannot pass objects of
non-trivially-copyable type 'std::string {aka class
std::basic_string}' through '...' 11:5: note: in expansion of
macro 'LOG_NOTE'
The issue here is not the variadic macro, but the call to printf. Have a look at the documentation: the format specifier "%s" corresponds to char*, not std::string. printf can only handle primitive builtin types. You can change you invocation to
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
to fix this.
I wrote a variadic macro
Don't. Use a variadic template function.
The actual problem you have is that you're trying to pass a C++ object (std::string) through a C API (printf). This is not possible.
You'd need some mechanism for conversion, for example:
#include <stdio.h>
#include <string>
template<class T>
decltype(auto) convert_for_log_note(T const& x)
{
return x;
}
decltype(auto) convert_for_log_note(std::string const& x)
{
return x.c_str();
}
template<class...Args>
void LOG_NOTE(const char* format, Args&&...args)
{
printf(format, convert_for_log_note(args)...);
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
return 0;
}
Example output:
Hello World 5 random string
http://coliru.stacked-crooked.com/a/beb3431114833860
Update:
For C++11 you'll need to spell out the return types by hand:
#include <stdio.h>
#include <string>
template<class T>
T const& convert_for_log_note(T const& x)
{
return x;
}
const char* convert_for_log_note(std::string const& x)
{
return x.c_str();
}
template<class...Args>
void LOG_NOTE(const char* format, Args&&...args)
{
printf(format, convert_for_log_note(args)...);
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
return 0;
}
You cannot pass object to printf, so you have currently to use
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
If you don't need formatting, and just write every argument separated with space, you might simply use variadic template instead of MACRO:
template <typename ... Ts>
void LOG_NOTE(const Ts&...args)
{
const char* sep = "";
(((std::cout << sep << args), sep = " "), ...); // C++17 folding expression
// C++11/C++14 version are more verbose:
// int dummy[] = {0, ((std::cout << sep << args), (sep = " "), 0)...};
// static_cast<void>(dummy); // avoid warning for unused variable
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("Hello World", bar, foo);
}
Demo
I could not get #richardhodges nice solution to work in any of the C++11 compilers I tried. However, the following works with gcc -std=c++11:
#include <stdio.h>
#include <string>
template<class T>
T convert_for_log_note(T const& x)
{
return x;
}
inline const char* convert_for_log_note(std::string const& x)
{
return x.c_str();
}
template<class...Args>
void LOG_NOTE(const char* format, Args&&...args)
{
printf(format, convert_for_log_note(args)...);
}
int main()
{
std::string foo = "random string";
int bar = 5;
LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
return 0;
}
The inline keyword is necessary with the above solution for the Arduino C++ compiler, whereas other g++ compilers do not require it (the ones I have tried, anyways). Without this keyword, the Arduino code compiles, but the linker complains about multiple definitions.

How to create a macro for a templated function in c++

I want to create a macro for a template function
something on the lines of
#define CHECK_MIN(T)(value, target) checkmin(T value, T target) \
checkmin<T>(T val, T target, __FUNCTION__, __LINE__)
template<typename T> checkmin(T val, T target, const char* functionName, long lineNumber)
{
// Check if the val is less than target
// Construct a std::string using the function name and line name
// throw std:: exception passing the string constructed above.
}
I am not able to get the syntax to achieve this. Any ideas?
You can do it by following way:
#include <iostream>
using namespace std;
template<typename T>
void checkmin(T val, T target, const char* functionName, long lineNumber)
{
// Check if the val is less than target
// Construct a std::string using the function name and line name
// throw std:: exception passing the string constructed above.
}
#define CHECK_MIN(value, target) \
checkmin(value,target, __FUNCTION__, __LINE__);
int main() {
/*Not using new in object definitions so I don't have to delete them afterwards since pointers don't stay in memory*/
CHECK_MIN(5,6);
return 0;
}

How can I wrap a function with variable length arguments?

I am looking to do this in C/C++. I came across Variable Length Arguments, but this suggests a solution with Python and C using libffi.
Now, if I want to wrap the printf function with myprintf.
I do it like below:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
printf(fmt, args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n", a, v, b);
return 0;
}
But the results are not as expected!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
What did I miss?
The problem is that you cannot use 'printf' with va_args. You must use vprintf if you are using variable argument lists. vprint, vsprintf, vfprintf, etc. (there are also 'safe' versions in Microsoft's C runtime that will prevent buffer overruns, etc.)
You sample works as follows:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n", a, v, b);
return 0;
}
In C++11, this is one possible solution using variadic templates:
template<typename... Args>
void myprintf(const char* fmt, Args... args)
{
std::printf(fmt, args...);
}
As rubenvb points out, there are trade-offs to consider. For example, you will be generating code for each instance which will lead to code bloat.
I am also unsure what you mean by pure.
In C++ we use:
#include <cstdarg>
#include <cstdio>
class Foo
{
void Write(const char* pMsg, ...);
};
void Foo::Write( const char* pMsg, ...)
{
char buffer[4096];
std::va_list arg;
va_start(arg, pMsg);
std::vsnprintf(buffer, 4096, pMsg, arg);
va_end(arg);
...
}
Actually, there's a way to call a function that doesn’t have a va_list version from a wrapper. The idea is to use assembler, do not touch arguments on the stack, and temporarily replace the function return address.
An example for Visual C x86. call addr_printf calls printf():
__declspec( thread ) static void* _tls_ret;
static void __stdcall saveret(void *retaddr) {
_tls_ret = retaddr;
}
static void* __stdcall _getret() {
return _tls_ret;
}
__declspec(naked)
static void __stdcall restret_and_return_int(int retval) {
__asm {
call _getret
mov [esp], eax ; /* replace current retaddr with saved */
mov eax, [esp+4] ; /* retval */
ret 4
}
}
static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) {
printf("calling printf(\"%s\")\n", fmt);
}
static void __stdcall _dbg_printf_end(int ret) {
printf("printf() returned %d\n", ret);
}
__declspec(naked)
int dbg_printf(const char *fmt, ...)
{
static const void *addr_printf = printf;
/* prolog */
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
nop
}
{
va_list args;
va_start(args, fmt);
_dbg_printf_beg(fmt, args);
va_end(args);
}
/* epilog */
__asm {
mov esp, ebp
pop ebp
}
__asm {
call saveret
call addr_printf
push eax
push eax
call _dbg_printf_end
call restret_and_return_int
}
}
Are you using C or C++? The next C++ version, C++0x, will support variadic templates which provide a solution to that problem.
Another workaround can be achieved by clever operator overloading to achieve a syntax like this:
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
In order to get this to work, the class varargs has to be implemented to override operator = that returns a proxy object which, in turn, overrides operator ,. However, making this variant type safe in current C++ isn't possible as far as I know since it would have to work by type erasure.
How do you mean a pure C/C++ solution?
The rest parameter (...) is supported cross platform in the C runtime.
va_arg, va_copy, va_end, va_start
void myprintf(char* fmt, ...)
{
va_ list args;
va_ start(args, fmt);
printf(fmt, args); // This is the fault. "vprintf(fmt, args);"
// should have been used.
va_ end(args);
}
If you're just trying to call printf,
there's a printf variant called vprintf that takes
the va_list directly: vprintf(fmt, args);