I've made some variadic output macros, especially for test output purposes. Examples: C++-code // output: function file(first and last char) linenumber variable=value
L(i,b); // result e.g. {evenRCpower@ih2943: i=36 b=1 @}
L(hex,pd,dec,check.back(),even,"e.g.");
// result e.g.: {way@ih3012: pd=babbbaba check.back()=15216868001 even=1 e.g. @}
They are working fine, having some minor restrictions, which can be lowered by more text analysis (restricted usage of comma and output specifications). Up to now, they are working at least under g++1z and c++1z. My questions:
#define WO_IS 1
#define L(...) locate(__FILE__,__LINE__,__func__,"\n{",": "," @}\n",#__VA_ARGS__,##__VA_ARGS__)
string argTExcludes = " hex dec std::hex std::dec ", funcExcludes = " setprecision ";
string argT(const string sarg,int &nextpos) { // NO exact analysis!! Simple strings and chars allowed, but parameters with commata like in f(x,y,"A,",','): crazy output !!
int i = nextpos+1, pos = i, follow = 0; string nom; bool apo = false;
for (; i < sarg.size(); i++)
if(sarg.at(i) == ' ') { ;
} else if ((sarg.at(i) == ',')||(i == (sarg.size()-1))) {
nom = sarg.substr(pos,i-pos);
if (argTExcludes.find(nom) != std::string::npos) { nextpos = i; return ""; };
break;
} else {
if ((sarg.at(i) != ' ') && (!follow++) && ((sarg.at(i) == '"')||(sarg.at(i) == '\'')) ) apo = true;
if ((sarg.at(i) == '"') && ( (i==0)||((i > 0) && (sarg.at(i-1) != '\'')) )) { i++; while ((sarg.at(i) != '"') && (i < sarg.size())) i++; };
if (sarg.at(i) == '(') {
nom = sarg.substr(pos,i-pos); if (funcExcludes.find(nom) != std::string::npos) apo = true;
};
};
nextpos = i;
return (apo)?"":sarg.substr(pos,i-pos)+"=";
};
template <typename... Ts>
inline void locate(string ort,long line,string funct,const string prefix, const string trenner, const string postfix, string sarg, Ts... args)
{
#if WO_IS > 0 // all range restrictions abandoned
int pos = -1; bool apo; sarg += ",";
std::ios_base::fmtflags f( cout.flags() );
std::cout << prefix << funct << '@' << ort[0] << ort.back() << dec << line << trenner;
cout.flags( f );
((std::cout << argT(sarg,pos) << args << ' '), ...); // forbidden: endl - it will give syntax error even when no blank at end
// ((std::cout << argT(sarg,pos); std::cout << args; std::cout << ' '), ...); // forbidden: endl - it will also give syntax error
if (postfix == "\0") std::cout.flush();
else std::cout << postfix; // << endl;
#endif
};
Is there a solution needing no textual analysis, e.g. to avoid
#VA_ARGS
and get the names for every parameter separated?
You might do it with hard coded limit.
Some utilities MACRO to "iterate" over __VA_ARGS__
:
#define COUNT_VA_ARGS(...) TAKE_10(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define TAKE_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, N,...) N
#define JOIN(a, b) JOIN_H(a, b)
#define JOIN_H(a, b) a ## b
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap, x0) wrap(x0)
#define WRAP_VA_ARGS_2(wrap, x0, x1) wrap(x0), wrap(x1)
#define WRAP_VA_ARGS_3(wrap, x0, x1, x2) wrap(x0), wrap(x1), wrap(x2)
#define WRAP_VA_ARGS_4(wrap, x0, x1, x2, x3) wrap(x0), wrap(x1), wrap(x2), wrap(x3)
#define WRAP_VA_ARGS_5(wrap, x0, x1, x2, x3, x4) wrap(x0), wrap(x1), wrap(x2), wrap(x3), wrap(x4)
// ...
// Call into one of the concrete ones above
#define WRAP_VA_ARGS(wrap, ...) JOIN(WRAP_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS__))(wrap, __VA_ARGS__)
Then, you might do something like
template <typename... Ts>
void print(std::source_location loc, const Ts&...args)
{
std::cout << loc.function_name() << ":" << loc.line() << std::endl;
((std::cout << args << " "), ...) << std::endl;
}
#define NAMED_PAIR(x) #x, x
#define PRINT(...) print(std::source_location::current(), WRAP_VA_ARGS(NAMED_PAIR, __VA_ARGS__))