How to forward the macro args to format func? - c++

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

Related

How to overload a function taking variable number of arguments

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

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 can I make this more robust, given that there is no reflection in C++?

I have the following class:
template <typename T>
T get_value(std::string name, T defaultValue)
{
// read values from config file and assign to var; if not found use defaultValue
return defaultValue;
}
class A {
public:
A()
: myvar1_(get_value("myvar1", 0))
, myvar2_(get_value("myvar2", 1.5))
{
}
int myvar1_;
double myvar2_;
std::string asString() const {
std::stringstream str;
str << "myvar1 = " << myvar1_
<< ", myvar2 = " << myvar2_;
return str.str();
}
private:
// other things exist here, unrelated to myvar1/2
};
Where get_value is a function that reads the values of the variables from some config file, and if not found uses the default value. I also have myvar1_ and myvar2_ as public member variables, because they get accessed directly, and I'd like to keep that feature and do not want to add a potential function hop.
Now, you can see that I have typed myvar1 or myvar1_ in quite a few different places, and I'd like to make this more robust, so that I can type, somewhere somehow, myvar1_, "myvar1", 0 once (as opposed to having typed myvar1_ 3 times and "myvar1" twice), and automagically get the above functions called and values filled. I have a lot of variables and they get added or removed fairly frequently, and sometimes I forget to initialize them, or mistype the string name in set_value, or forget to add a new variable to asString.
Is that possible to do? I'd appreciate any hints.
Option 1: A DSL that generates your code from definitions.
Option 2: Use a class facade over the config lookup, backed presumably by access to your config class, and combine with C++11's member initialization. This will reduce the number of times you have to repeat a variable, which can then be hidden with a little macroing if you're up for that.
#include <iostream>
template<typename T>
class DV {
T value_;
public:
DV(const char* name, const T& defaultValue)
{
// read values from config file and assign to var; if not found use defaultValue
value_ = defaultValue;
}
operator const T& () const { return value_; }
};
using DVInt = DV<int>;
using DVStr = DV<const char*>;
struct A {
int i_ = DVInt("i", 42);
const char* str_ = DVStr("str", "hello");
A() = default;
};
int main() {
A a;
std::cout << a.i_ << ", " << a.str_ << "\n";
}
Demo: http://ideone.com/RAyKwI
-- Edit --
Reducing to one instance with a macro.
Instead of the using statements:
#include <iostream>
template<typename T>
class DV {
T value_;
public:
DV(const char* name, const T& defaultValue)
{
// read values from config file and assign to var; if not found use defaultValue
value_ = defaultValue;
}
operator const T& () const { return value_; }
};
#define CONCAT(x, y) x ## y
#define DVDecl(T, name, def) T CONCAT(name, _) = DV<T>(#name, def)
#define DVInt(name, def) DVDecl(int, name, def)
#define DVCStr(name, def) DVDecl(const char*, name, def)
struct A {
DVInt(myvar1, 42);
DVCStr(myvar2, "hello");
A() = default;
};
int main() {
A a;
std::cout << a.myvar1_ << ", " << a.myvar2_ << "\n";
}
http://ideone.com/JmgfH9
This doesn't eliminate your need to manually add them to asString, though.
I ended up with a macro solution, heavily borrowing from a different SO answer:
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))
#define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))
#define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
#define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
#define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_GET_END0() 0, MAP_END
#define MAP_GET_END1(...) 0
#define GET_MACRO(_0, _1, _2, _3, _4, NAME, ...) NAME
#define MAP_GET_END(...) GET_MACRO(_0, ##__VA_ARGS__, MAP_GET_END1, MAP_GET_END1, MAP_GET_END1, MAP_GET_END1, MAP_GET_END0)(__VA_ARGS__)
#define MAP_NEXT0(item, next, ...) next MAP_OUT
#define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0)
#define MAP_NEXT(item, next) MAP_NEXT1 (MAP_GET_END item, next)
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
#define DEFINE_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
TYPE NAME##_;
#define DEFINE_VARS(TUPLE) DEFINE_VARS_T TUPLE
#define CONSTRUCT_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
NAME##_ = get_value(#NAME, DEFAULT_VALUE);
#define CONSTRUCT_VARS(TUPLE) CONSTRUCT_VARS_T TUPLE
#define PRINT_VARS_T(TYPE, NAME, DEFAULT_VALUE) \
<< #NAME << " = " << NAME##_ << ", "
#define PRINT_VARS(TUPLE) PRINT_VARS_T TUPLE
#define CONFIG_VARS(...) \
MAP(DEFINE_VARS, __VA_ARGS__) \
A() { \
MAP(CONSTRUCT_VARS, __VA_ARGS__) \
} \
std::string asString() const { \
std::stringstream str; \
str MAP(PRINT_VARS, __VA_ARGS__); \
return str.str(); \
}
template <typename T>
T get_value(std::string name, T defaultValue)
{
// read values from config file and assign to var
return defaultValue;
}
class A {
public:
CONFIG_VARS
(
(int, myvar1, 0),
(double, myvar2, 1.5),
(std::string, myvar3, "what")
)
private:
// other things exist here, unrelated to myvar1/2
};

Error:.. 'va_list' has not been declared

I'v been trying to compile flann but this error shows up! ''va_list' has not been declared'
Can any one help me to solve this error?
Plz guild me so simple if u can, I'm really new in programming!
In file included from ./flann/nn/index_testing.h:41,
from ./flann/flann.hpp:43,
from src/common.hpp:12,
from src/main.cpp:9:
./flann/util/logger.h:74: error: 'va_list' has not been declared
Makefile:43: recipe for target `src/main.o' failed
make: *** [src/main.o] Error 1
Here is logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <cstdio>
#include "flann/general.h"
namespace flann
{
class Logger
{
FILE* stream;
int logLevel;
public:
Logger() : stream(stdout), logLevel(LOG_WARN) {};
~Logger()
{
if (stream!=NULL && stream!=stdout) {
fclose(stream);
}
}
void setDestination(const char* name)
{
if (name==NULL) {
stream = stdout;
}
else {
stream = fopen(name,"w");
if (stream == NULL) {
stream = stdout;
}
}
}
void setLevel(int level) { logLevel = level; }
int log(int level, const char* fmt, ...);
int log(int level, const char* fmt, va_list arglist);
int fatal(const char* fmt, ...);
int error(const char* fmt, ...);
int warn(const char* fmt, ...);
int info(const char* fmt, ...);
};
extern Logger logger;
}
#endif //LOGGER_H
You miss an include for the relevant macros
#include <cstdarg>