#include <bits/stdc++.h>
using namespace std;
#define __deb(X...) (cout << "[" << #X << "]:" << X)
template <typename... type>
void debug(type &&... args)
{
((__deb(args)), ...);
}
int main()
{
int a = 1, b = 3;
debug(a,b);
return 0;
}
I got output like [args]:1[args]:3
but I wanted output like [a]:1[b]:3
One way could be to quote all the macro arguments using #__VA_ARGS__ and parse that string in the C++ function.
Example:
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
template<typename T, typename... Args>
std::string debug_detail(const char* names, T&& var, Args&&... args) {
std::ostringstream builder;
// find variable end
const char* end = names;
while(*end != ',' && *end != '\0') ++end;
// display one variable
(builder << ' ').write(names, end - names) << '=' << var;
// continue parsing?
if constexpr(sizeof...(Args) > 0) {
// recursively call debug_detail() with the new beginning for names
builder << debug_detail(end + 1, std::forward<Args>(args)...);
}
return builder.str();
}
template<typename... Args>
void debug_entry(const char* file, int line, const char* func,
const char* names, Args&&... args) {
std::ostringstream retval;
// common debug info
retval << file << '(' << line << ") " << func << ':';
// add variable info
retval << debug_detail(names, std::forward<Args>(args)...) << '\n';
std::cout << retval.str();
}
// the actual debug macro
#define debug(...) \
debug_entry(__FILE__,__LINE__,__func__,#__VA_ARGS__,__VA_ARGS__)
int main() {
int foo = 1;
const double bar = 2;
const std::string Hello = "world";
debug(foo,bar,Hello);
}
Possible output:
example.cpp(49) main: foo=1 bar=2 Hello=world
Demo
A C++20 version using std::source_location:
#include <source_location>
template<typename... Args>
void debug_entry(const std::source_location location,
const char* names, Args&&... args) {
std::ostringstream retval;
// common debug info
retval << location.file_name() << '(' << location.line() << ','
<< location.column() << ") " << location.function_name() << ':';
// add variable info
retval << debug_detail(names, std::forward<Args>(args)...) << '\n';
std::cout << retval.str();
}
// the actual debug macro
#define debug(...) \
debug_entry(std::source_location::current(), #__VA_ARGS__,__VA_ARGS__)
Demo
Here's my humble attempt, which uses a macro FOO to create a pair of the variable name and its value, and passes the arguments to a variadic function:
#include <utility>
#include <iostream>
#define FOO(var) std::make_pair(std::string(#var), var)
template <typename T>
void __deb(std::pair<std::string, T> arg) { std::cout << "[" << arg.first << "]:" << arg.second; }
template <typename... type>
void debug(std::pair<std::string, type> &&... args)
{
(__deb(args), ...);
}
int main()
{
int a = 1, b = 3;
debug(FOO(a), FOO(b));
}
Demo
Alternatively, to avoid having a macro call FOO for each variable in debug, you could define debug as a macro that accepts #__VA_ARGS__ (string of arguments) and __VA_ARGS__ (argument values). Then parse each variable name and value:
#include <iostream>
#include <sstream>
#include <stdio.h>
#define debug(...) debug_print(#__VA_ARGS__,__VA_ARGS__)
template <typename T>
void __deb(std::istringstream &ss, T arg)
{
//Extract name from stream
std::string name;
std::getline(ss, name, ',');
//trim leading space
const auto pos(name.find_first_not_of(" "));
name.erase(0, pos);
std::cout << "[" << name << "]:" << arg;
}
template <typename... type>
void debug_print(const char* names, type&&...args)
{
std::istringstream ss(names);
(__deb(ss, args), ...);
}
int main()
{
int a = 1, b = 3, c = 4;
debug(a, b, c);
}
Demo
The problem is that the MACRO is used in the context of void debug(type &&... args), which is not familiar with the names a and b.
A possible solution to your problem is to implement a bigger MACRO which gats several vars and calls a sub-MACRO which handles a single var (which you already implemented).
This way the initial MACRO call will happen in the context of the calling function which has the wanted vars
Related
DEMO
How I could read the args of this variadic template, as it can be any number of arguments?
#include <tuple>
#include <string>
#include <iostream>
enum class traceErr
{
err1, err2
};
template <typename... Args>
void TraceErr(traceErr err, Args&&... args)
{
auto pack = std::make_tuple(std::forward<Args>(args)...);
std::string str;
switch (err)
{
case traceErr::err1:
str = "Err: " + std::to_string(std::get<0>(pack));
break;
case traceErr::err2:
str = "Err: " + std::to_string(std::get<0>(pack)) + " "
+ std::get<1>(pack);
break;
}
std::cout << str << std::endl;
}
int main()
{
TraceErr(traceErr::err1, 1);
TraceErr(traceErr::err2, 2, "etc");
}
In the example above I'm getting these errors:
static_assert failed: 'tuple index out of bounds
'get': no matching overload function found
Because the number of args being passed are different, how i could 'fix' it to work with any number of args?
For every specialization of TraceErr the whole function body is compiled and must be valid. If you're providing only 2 parameters and therefore the tuple size is 1, the expression
std::get<1>(pack)
is invalid.
The only way to ignore this kind of issue and keep the statement for both specializations would be to use if constexpr, but this requires the first function parameter to be changed to a template parameter.
enum class traceErr
{
err1, err2
};
template <traceErr err, typename... Args>
void TraceErr(Args&&... args)
{
auto pack = std::make_tuple(std::forward<Args>(args)...);
std::string str;
if constexpr (err == traceErr::err1)
{
str = "Err: " + std::to_string(std::get<0>(pack));
}
else
{
str = "Err: " + std::to_string(std::get<0>(pack)) + " "
+ std::get<1>(pack);
}
std::cout << str << std::endl;
}
int main()
{
TraceErr<traceErr::err1>(1);
TraceErr<traceErr::err2>(2, "etc");
}
But why use std::tuple here at all? Just use a fold expression:
template <typename T, typename... Args>
void TraceErr(T&& arg, Args&&... args)
{
std::cout << "Err: " << std::forward<T>(arg);
((std::cout << ' ' << std::forward<Args>(args)), ...);
std::cout << '\n';
}
int main()
{
TraceErr(1);
TraceErr(2, "etc");
}
(Note: You could generate a fold expression for creating a string, but it may be preferrable to avoid the dynamic memory allocation required and directly write the results to the output stream.)
I am looking for a functionality that allows logging a function input arguments. For example:
void func(std::string& input_name, const int n){
// print current function's inputs' type, name, and value
}
which after the function is called, the following will be printed or readable as string,
input1:
type: std::string,
name: input_name,
value: "something",
input2:
type: int,
name: n,
value: 12,
does anyone have a suggestion for such goal
------------edit
it is also ok for me if printing the type or name is not possible.
I am flexible with a solution close to this, like if we can get a list of input arguments, or etc.
Printing a variable's type and value is fairly simple. But variable names don't exist at runtime, so the only way to obtain a variable's name as a string, without hard-coding it, is to use a macro at compile-time. Macros have a feature to stringify tokens.
Try something like this (sorry, this is from memory, I can't get to a compiler at the moment, I'll update this later today):
#include <iomanip>
#include <typeinfo>
#include <type_traits>
template <typename T>
std::string getTypeName()
{
// TODO: to get a more human-readable output, use
// if-constexpr, or template specialization, or one
// of the solutions from https://stackoverflow.com/q/281818/65863...
return typeid(T).name();
}
template<typename T>
std::string stringify(const T& param)
{
std::ostringstream oss;
if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, char*> || std::is_same_v<T, const char*>)
oss << std::quoted(param);
else
oss << param;
return oss.str();
}
template <typename T>
void doLog(int num, std::string_view name, const T& value)
{
std::cout << "input" << num << ":" << std::endl
<< "\ttype: " << getTypeName<T>() << "," << std::endl
<< "\tname: " << #param << "," << std::endl
<< "\tvalue: " << stringify(param) << "," << std::endl;
}
#define LOG(num, param) doLog(num, #param, param)
void func(std::string& input_name, const int n){
LOG(1, input_name)
LOG(2, n)
}
This is a start using templates instead of macros :
(Indeed with the added notion that typeid.name can output mangled names)
#include <iostream>
#include <string>
#include <sstream>
namespace details
{
//-------------------------------------------------------------------------
// formatters for parameters to functions
//
template<typename type_t>
std::string format(const type_t& value)
{
std::ostringstream os;
os << typeid(type_t).name() << ":";
os << value;
return os.str();
}
// output boolean as text
std::string format(const bool& value)
{
return value ? std::string("bool:true") : std::string("bool:false");
}
// add quotes to string
std::string format(const char* value)
{
std::ostringstream os;
os << "const char*:\"";
os << value;
os << "\"";
return os.str();
}
// recursively log all parameters
template<typename arg_t, typename... args_t>
inline void log_parameters(bool comma, const arg_t& arg, args_t... args)
{
if (comma) std::cout << ", ";
std::cout << format(arg);
if constexpr (sizeof...(args_t) > 0)
{
// true is print separating comma at next call
log_parameters(true, std::forward<args_t>(args)...);
}
}
//-------------------------------------------------------------------------
template<typename... args_t>
inline void log(const char* function, args_t... args)
{
std::cout << "function call to : " << function << "(";
// if there are any arguments to log do so
if constexpr (sizeof...(args_t) > 0)
{
// false == do not print a comma on first call
log_parameters(false, std::forward<args_t>(args)...);
}
std::cout << ");\n";
}
}
void my_function(const bool x, const int y, const char* str)
{
details::log(__FUNCTION__, x, y, str);
}
int main()
{
my_function(1, 42, "Hello world!");
}
#include <bits/stdc++.h>
using namespace std;
#define __deb(X...) (cout << "[" << #X << "]:" << X)
template <typename... type>
void debug(type &&... args)
{
((__deb(args)), ...);
}
int main()
{
int a = 1, b = 3;
debug(a,b);
return 0;
}
I got output like [args]:1[args]:3
but I wanted output like [a]:1[b]:3
One way could be to quote all the macro arguments using #__VA_ARGS__ and parse that string in the C++ function.
Example:
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
template<typename T, typename... Args>
std::string debug_detail(const char* names, T&& var, Args&&... args) {
std::ostringstream builder;
// find variable end
const char* end = names;
while(*end != ',' && *end != '\0') ++end;
// display one variable
(builder << ' ').write(names, end - names) << '=' << var;
// continue parsing?
if constexpr(sizeof...(Args) > 0) {
// recursively call debug_detail() with the new beginning for names
builder << debug_detail(end + 1, std::forward<Args>(args)...);
}
return builder.str();
}
template<typename... Args>
void debug_entry(const char* file, int line, const char* func,
const char* names, Args&&... args) {
std::ostringstream retval;
// common debug info
retval << file << '(' << line << ") " << func << ':';
// add variable info
retval << debug_detail(names, std::forward<Args>(args)...) << '\n';
std::cout << retval.str();
}
// the actual debug macro
#define debug(...) \
debug_entry(__FILE__,__LINE__,__func__,#__VA_ARGS__,__VA_ARGS__)
int main() {
int foo = 1;
const double bar = 2;
const std::string Hello = "world";
debug(foo,bar,Hello);
}
Possible output:
example.cpp(49) main: foo=1 bar=2 Hello=world
Demo
A C++20 version using std::source_location:
#include <source_location>
template<typename... Args>
void debug_entry(const std::source_location location,
const char* names, Args&&... args) {
std::ostringstream retval;
// common debug info
retval << location.file_name() << '(' << location.line() << ','
<< location.column() << ") " << location.function_name() << ':';
// add variable info
retval << debug_detail(names, std::forward<Args>(args)...) << '\n';
std::cout << retval.str();
}
// the actual debug macro
#define debug(...) \
debug_entry(std::source_location::current(), #__VA_ARGS__,__VA_ARGS__)
Demo
Here's my humble attempt, which uses a macro FOO to create a pair of the variable name and its value, and passes the arguments to a variadic function:
#include <utility>
#include <iostream>
#define FOO(var) std::make_pair(std::string(#var), var)
template <typename T>
void __deb(std::pair<std::string, T> arg) { std::cout << "[" << arg.first << "]:" << arg.second; }
template <typename... type>
void debug(std::pair<std::string, type> &&... args)
{
(__deb(args), ...);
}
int main()
{
int a = 1, b = 3;
debug(FOO(a), FOO(b));
}
Demo
Alternatively, to avoid having a macro call FOO for each variable in debug, you could define debug as a macro that accepts #__VA_ARGS__ (string of arguments) and __VA_ARGS__ (argument values). Then parse each variable name and value:
#include <iostream>
#include <sstream>
#include <stdio.h>
#define debug(...) debug_print(#__VA_ARGS__,__VA_ARGS__)
template <typename T>
void __deb(std::istringstream &ss, T arg)
{
//Extract name from stream
std::string name;
std::getline(ss, name, ',');
//trim leading space
const auto pos(name.find_first_not_of(" "));
name.erase(0, pos);
std::cout << "[" << name << "]:" << arg;
}
template <typename... type>
void debug_print(const char* names, type&&...args)
{
std::istringstream ss(names);
(__deb(ss, args), ...);
}
int main()
{
int a = 1, b = 3, c = 4;
debug(a, b, c);
}
Demo
The problem is that the MACRO is used in the context of void debug(type &&... args), which is not familiar with the names a and b.
A possible solution to your problem is to implement a bigger MACRO which gats several vars and calls a sub-MACRO which handles a single var (which you already implemented).
This way the initial MACRO call will happen in the context of the calling function which has the wanted vars
#include <bits/stdc++.h>
using namespace std;
#define __deb(X...) (cout << "[" << #X << "]:" << X)
template <typename... type>
void debug(type &&... args)
{
((__deb(args)), ...);
}
int main()
{
int a = 1, b = 3;
debug(a,b);
return 0;
}
I got output like [args]:1[args]:3
but I wanted output like [a]:1[b]:3
One way could be to quote all the macro arguments using #__VA_ARGS__ and parse that string in the C++ function.
Example:
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
template<typename T, typename... Args>
std::string debug_detail(const char* names, T&& var, Args&&... args) {
std::ostringstream builder;
// find variable end
const char* end = names;
while(*end != ',' && *end != '\0') ++end;
// display one variable
(builder << ' ').write(names, end - names) << '=' << var;
// continue parsing?
if constexpr(sizeof...(Args) > 0) {
// recursively call debug_detail() with the new beginning for names
builder << debug_detail(end + 1, std::forward<Args>(args)...);
}
return builder.str();
}
template<typename... Args>
void debug_entry(const char* file, int line, const char* func,
const char* names, Args&&... args) {
std::ostringstream retval;
// common debug info
retval << file << '(' << line << ") " << func << ':';
// add variable info
retval << debug_detail(names, std::forward<Args>(args)...) << '\n';
std::cout << retval.str();
}
// the actual debug macro
#define debug(...) \
debug_entry(__FILE__,__LINE__,__func__,#__VA_ARGS__,__VA_ARGS__)
int main() {
int foo = 1;
const double bar = 2;
const std::string Hello = "world";
debug(foo,bar,Hello);
}
Possible output:
example.cpp(49) main: foo=1 bar=2 Hello=world
Demo
A C++20 version using std::source_location:
#include <source_location>
template<typename... Args>
void debug_entry(const std::source_location location,
const char* names, Args&&... args) {
std::ostringstream retval;
// common debug info
retval << location.file_name() << '(' << location.line() << ','
<< location.column() << ") " << location.function_name() << ':';
// add variable info
retval << debug_detail(names, std::forward<Args>(args)...) << '\n';
std::cout << retval.str();
}
// the actual debug macro
#define debug(...) \
debug_entry(std::source_location::current(), #__VA_ARGS__,__VA_ARGS__)
Demo
Here's my humble attempt, which uses a macro FOO to create a pair of the variable name and its value, and passes the arguments to a variadic function:
#include <utility>
#include <iostream>
#define FOO(var) std::make_pair(std::string(#var), var)
template <typename T>
void __deb(std::pair<std::string, T> arg) { std::cout << "[" << arg.first << "]:" << arg.second; }
template <typename... type>
void debug(std::pair<std::string, type> &&... args)
{
(__deb(args), ...);
}
int main()
{
int a = 1, b = 3;
debug(FOO(a), FOO(b));
}
Demo
Alternatively, to avoid having a macro call FOO for each variable in debug, you could define debug as a macro that accepts #__VA_ARGS__ (string of arguments) and __VA_ARGS__ (argument values). Then parse each variable name and value:
#include <iostream>
#include <sstream>
#include <stdio.h>
#define debug(...) debug_print(#__VA_ARGS__,__VA_ARGS__)
template <typename T>
void __deb(std::istringstream &ss, T arg)
{
//Extract name from stream
std::string name;
std::getline(ss, name, ',');
//trim leading space
const auto pos(name.find_first_not_of(" "));
name.erase(0, pos);
std::cout << "[" << name << "]:" << arg;
}
template <typename... type>
void debug_print(const char* names, type&&...args)
{
std::istringstream ss(names);
(__deb(ss, args), ...);
}
int main()
{
int a = 1, b = 3, c = 4;
debug(a, b, c);
}
Demo
The problem is that the MACRO is used in the context of void debug(type &&... args), which is not familiar with the names a and b.
A possible solution to your problem is to implement a bigger MACRO which gats several vars and calls a sub-MACRO which handles a single var (which you already implemented).
This way the initial MACRO call will happen in the context of the calling function which has the wanted vars
I'm trying to generate an argument list for a function call during runtime, but I can't think of a way to accomplish this in c++.
This is for a helper library I'm writing. I'm taking input data from the client over a network and using that data to make a call to a function pointer that the user has set previously. The function takes a string(of tokens, akin to printf), and a varying amount of arguments. What I need is a way to add more arguments depending on what data has been received from the client.
I'm storing the functions in a map of function pointers
typedef void (*varying_args_fp)(string,...);
map<string,varying_args_fp> func_map;
An example usage would be
void printall(string tokens, ...)
{
va_list a_list;
va_start(a_list, tokens);
for each(auto x in tokens)
{
if (x == 'i')
{
cout << "Int: " << va_arg(a_list, int) << ' ';
}
else if(x == 'c')
{
cout << "Char: " << va_arg(a_list, char) << ' ';
}
}
va_end(a_list);
}
func_map["printall"] = printall;
func_map["printall"]("iic",5,10,'x');
// prints "Int: 5 Int: 10 Char: x"
This works nicely when hardcoding the function call and it's arguments, but if I've received the data "CreateX 10 20", the program needs to be able to make the argument call itself. eg
// func_name = "CreateX", tokens = 'ii', first_arg = 10, second_arg = 20
func_map[func_name](tokens,first_arg,second_arg);
I can't predict how users are going to lay out the functions and code this beforehand.
If anyone has suggestions on accomplishing this task another way, feel free to suggest. I need the user to be able to "bind" a function to the library, and for the library to call it later after it has received data from a networked client, a callback in essence.
Here is a C++11 solution. It does not support varargs functions like printall or printf, this is impossible with this technique and IMO impossible at all, or at the very least extremely tricky. Such function are difficult to use safely in an environment like yours anyway, since any bad request from any client could crash the server, with absolutely no recourse whatsoever. You probably should move to container-based interface for better safety and stability.
On the other hand, this method supports all (?) other functions uniformly.
#include <vector>
#include <iostream>
#include <functional>
#include <stdexcept>
#include <string>
#include <boost/any.hpp>
template <typename Ret, typename... Args>
Ret callfunc (std::function<Ret(Args...)> func, std::vector<boost::any> anyargs);
template <typename Ret>
Ret callfunc (std::function<Ret()> func, std::vector<boost::any> anyargs)
{
if (anyargs.size() > 0)
throw std::runtime_error("oops, argument list too long");
return func();
}
template <typename Ret, typename Arg0, typename... Args>
Ret callfunc (std::function<Ret(Arg0, Args...)> func, std::vector<boost::any> anyargs)
{
if (anyargs.size() == 0)
throw std::runtime_error("oops, argument list too short");
Arg0 arg0 = boost::any_cast<Arg0>(anyargs[0]);
anyargs.erase(anyargs.begin());
std::function<Ret(Args... args)> lambda =
([=](Args... args) -> Ret {
return func(arg0, args...);
});
return callfunc (lambda, anyargs);
}
template <typename Ret, typename... Args>
std::function<boost::any(std::vector<boost::any>)> adaptfunc (Ret (*func)(Args...)) {
std::function<Ret(Args...)> stdfunc = func;
std::function<boost::any(std::vector<boost::any>)> result =
([=](std::vector<boost::any> anyargs) -> boost::any {
return boost::any(callfunc(stdfunc, anyargs));
});
return result;
}
Basically you call adaptfunc(your_function), where your_function is a function of any type (except varargs). In return you get an std::function object that accepts a vector of boost::any and returns a boost::any. You put this object in your func_map, or do whatever else you want with them.
Types of the arguments and their number are checked at the time of actual call.
Functions returning void are not supported out of the box, because boost::any<void> is not supported. This can be dealt with easily by wrapping the return type in a simple template and specializing for void. I've left it out for clarity.
Here's a test driver:
int func1 (int a)
{
std::cout << "func1(" << a << ") = ";
return 33;
}
int func2 (double a, std::string b)
{
std::cout << "func2(" << a << ",\"" << b << "\") = ";
return 7;
}
int func3 (std::string a, double b)
{
std::cout << "func3(" << a << ",\"" << b << "\") = ";
return 7;
}
int func4 (int a, int b)
{
std::cout << "func4(" << a << "," << b << ") = ";
return a+b;
}
int main ()
{
std::vector<std::function<boost::any(std::vector<boost::any>)>> fcs = {
adaptfunc(func1), adaptfunc(func2), adaptfunc(func3), adaptfunc(func4) };
std::vector<std::vector<boost::any>> args =
{{777}, {66.6, std::string("yeah right")}, {std::string("whatever"), 0.123}, {3, 2}};
// correct calls will succeed
for (int i = 0; i < fcs.size(); ++i)
std::cout << boost::any_cast<int>(fcs[i](args[i])) << std::endl;
// incorrect calls will throw
for (int i = 0; i < fcs.size(); ++i)
try {
std::cout << boost::any_cast<int>(fcs[i](args[fcs.size()-1-i])) << std::endl;
} catch (std::exception& e) {
std::cout << "Could not call, got exception: " << e.what() << std::endl;
}
}
As already mentioned by #TonyTheLion, you can use boost::variant or boost::any to select between types at runtime:
typedef std::function<void(const std::string&, const std::vector<boost::variant<char, int>>&)> varying_args_fn;
std::map<std::string, varying_args_fn> func_map;
The you can e.g. use a static visitor to distinguish between the types. Here is a full example, note that the tokens parameter is actually no longer necessary since boost::variant knows at runtime what type is stored in it:
#include <map>
#include <vector>
#include <string>
#include <functional>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/any.hpp>
typedef std::function<void(const std::string&, const std::vector<boost::variant<char, int>>&)> varying_args_fn;
void printall(const std::string& tokens, const std::vector<boost::variant<char, int>>& args) {
for (const auto& x : args) {
struct : boost::static_visitor<> {
void operator()(int i) {
std::cout << "Int: " << i << ' ';
}
void operator()(char c) {
std::cout << "Char: " << c << ' ';
}
} visitor;
boost::apply_visitor(visitor, x);
}
}
int main() {
std::map<std::string, varying_args_fn> func_map;
func_map["printall"] = printall;
func_map["printall"]("iic", {5, 10, 'x'});
}