template<class Msg, class... Args>
std::wstring descf(Msg, Args&&... args) {
std::wostringstream woss;
owss << Msg << ". " << ... << " " << args << ": '" << args << "' ";//not legal at all
//or
owss << Msg << ". " << args[0] << ": '" << args[1] << "' " << args[2] << ": '" << args[3] << "' "; //... pseudo code, and so on...
}
I know I can just use a list of pairs or something like that instead, but I'm interested in how to do this while keeping the syntax of the function to:
const auto formatted = descf(L"message", "arg1", arg1, "arg2", arg2);
You can use a fold expression! It's not the prettiest*, but it's shorter than all the non-fold solutions presented:
template<class T, class ... Args>
std::wstring descf(T msg, Args&&... args) {
std::wostringstream owss;
owss << msg << ". ";
std::array<const char*, 2> tokens{": '", "' "};
int alternate = 0;
((owss << args << tokens[alternate], alternate = 1 - alternate), ...);
return owss.str();
}
Demo with sample output: https://godbolt.org/z/Gs8d2x
We perform a fold over the comma operator, where each operand is an output of one args and the alternating token, plus switching the token index (the latter two are combined with another comma operator).
*To a reader familiar with fold expressions (and the comma operator) this is probably the "best" code, but for everyone else it's utter gibberish, so use your own judgement whether you want to inflict this on your code base.
This is easy with a couple of helper functions that follow the following pattern.
void helper() {}
template <class T1, class T2, class ... T>
void helper(T1 t1, T2 t2, T ... t)
{
do_single_pair(t1, t2);
helper(t...);
}
This is not a fold expression but the net result is the same.
I suppose you can try with an index and a ternary operator.
Something as follows
template <typename ... Args>
std::wstring descf (std::wstring const & Msg, Args && ... args)
{
std::wostringstream woss;
int i = 0;
((woss << Msg << ". "), ... ,(woss << args << (++i & 1 ? ": '" : "' ")));
return woss.str();
}
The following code should do the trick. The parameter pack is expanded in an initializer list.
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
template <typename...Args>
std::string descf(std::string msg, Args &&... args)
{
auto argumentsVector = std::vector<std::string>{args...};
std::stringstream ss;
ss << msg << ". ";
for (auto i = std::size_t{0}; i < argumentsVector.size() - 1; ++i)
ss << argumentsVector[i] << ": '" << argumentsVector[i+1] << "' ";
auto result = ss.str();
if (!argumentsVector.empty())
result.pop_back();
return result;
}
int main()
{
std::cout << descf("message", "arg1", "1", "arg2", "2") << std::endl;
}
With std::index_sequence:
template <class Msg, class... Pairs>
std::wstring descf_pair(const Msg& msg, const Pairs&... pairs)
{
std::wstringstream woss;
woss << msg << ". ";
auto sep = L"";
((woss << sep << std::get<0>(pairs) << L": '"
<< std::get<1>(pairs) << L"'", sep = L" "), ...);
return woss.str();
}
template <class Msg, std::size_t... Is, class Tuple>
decltype(auto) descf_impl(const Msg& msg, std::index_sequence<Is...>, Tuple&& t)
{
return descf_pair(msg, std::tie(std::get<2 * Is>(t), std::get<2 * Is + 1>(t))...);
}
template <class Msg, typename ... Ts>
std::wstring descf(const Msg& msg, const Ts&... ts)
{
static_assert(sizeof...(Ts) % 2 == 0);
return descf_impl(msg,
std::make_index_sequence<sizeof...(Ts) / 2>(),
std::tie(ts...));
}
Demo
Related
I have a small bit of code.
I want to know if i could cut out my ProcessArg function
I would like to loop the parameter pack from inside my call function.
Im i best to use a lambda function in initializer_list or something else, if so how do i do it.
Thanks
template <class R, class Arg>
R ProcessArg(Arg&& arg) {
std::cout << typeid(arg).name() << (std::is_reference<Arg>::value ? "&" : "") << std::endl;
//std::cout << std::boolalpha << std::is_reference<Arg>::value << std::endl; // not always true
return R();
}
template <typename R, typename... Args>
R CallFunction(Args&&... args) {
std::size_t size = sizeof...(Args);
std::initializer_list<R>{ProcessArg<R>(std::forward<Args>(args)) ...};
return R();
}
template<typename Fn> class FunctionBase;
template<typename R, typename... Args>
class FunctionBase <R(*)(Args...)> {
public:
FunctionBase() {}
R operator()(Args&&... args) { // Args&& is a universal reference
return CallFunction<R>(std::forward<Args>(args)...);
}
};
int foo(int a, int& b) {
std::cout << std::boolalpha << std::is_reference<decltype(a)>::value << std::endl; // falae
std::cout << std::boolalpha << std::is_reference<decltype(b)>::value << std::endl; // true
return a + b;
}
int main() {
int in = 10;
foo(1, in);
FunctionBase<decltype(&foo)> func;
func(1, in);
}
With c++17 fold-expressions, you can replace:
std::initializer_list<R>{ProcessArg<R>(std::forward<Args>(args)) ...};
with:
((std::cout << typeid(args).name()
<< (std::is_reference<Args>::value ? "&" : "")
<< std::endl
), ...);
Or with a lambda expression to improve readability:
auto process = [](auto&& arg) {
std::cout << typeid(arg).name()
<< (std::is_lvalue_reference<decltype(arg)>::value ? "&" : "")
<< std::endl;
};
(process(std::forward<Args>(args)), ...);
In c++20:
auto process = [] <typename Arg> (Arg&& arg) {
std::cout << typeid(arg).name()
<< (std::is_reference<Arg>::value ? "&" : "")
<< std::endl;
};
(process(std::forward<Args>(args)), ...);
I need a function that receives variadic template arguments and returns a JSON array string.
I was able to reach this point:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
template<typename Arg>
void add_value(std::stringstream &result, Arg arg){
if (result.rdbuf()->in_avail())
result << ",";
if (std::is_same_v<Arg, string>) {
result << "\"" << arg << "\"";
} else if (std::is_same_v<Arg, const char*>) {
result << "\"" << arg << "\"";
} else if (std::is_same_v<Arg, char*>) {
result << "\"" << arg << "\"";
} else if (std::is_same_v<Arg, bool>) {
result << arg;
} else {
result << arg;
}
}
template<typename... Args>
std::string to_json_array(Args... args) {
std::stringstream result;
(add_value(result, args), ...);
return "[" + result.str() + "]";
}
int main() {
std::string result;
char aa[10] = "haha";
char *bb = aa;
result = to_json_array(123, "Hello", 1.5, string("World!"), true, aa, false, bb, NULL);
std::cout << result << "\n";
}
But it did not work for booleans and null.
As you can notice, I am not a C++ developer.
If you know another method, you can share it too.
I am sharing the solution I've got with the help of the comments:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
template<typename Arg>
void add_value(std::stringstream &result, Arg arg){
if (result.rdbuf()->in_avail())
result << ",";
if constexpr (std::is_same_v<Arg, string>) {
result << "\"" << arg << "\"";
} else if constexpr (std::is_same_v<Arg, const char*>) {
result << "\"" << arg << "\"";
} else if constexpr (std::is_same_v<Arg, char*>) {
result << "\"" << arg << "\"";
} else if constexpr (std::is_same_v<Arg, bool>) {
result << std::boolalpha;
result << arg;
result << std::noboolalpha;
} else if constexpr (std::is_same_v<Arg, nullptr_t>) {
result << "null";
} else {
result << arg;
}
}
template<typename... Args>
std::string to_json_array(Args... args) {
std::stringstream result;
(add_value(result, args), ...);
return "[" + result.str() + "]";
}
int main() {
std::string result;
char aa[10] = "haha";
char *bb = aa;
result = to_json_array(123, "Hello", 1.5, string("World!"), true, aa, false, bb, nullptr, 0);
std::cout << result << "\n";
}
Thank you all!
#include <iostream>
using namespace std;
template<class... Ts>
void Fun(Ts... ts)
{
cout <<"TS: "; ((cout<<ts<<", "), ..., (cout<<endl));
}
int main()
{
Fun(1,'a',"blah", 2.13, 3.14f); //console: 1, a, blah, 2.13, 3.14,
}
I want to remove the , separator after 3.14, how can I do that?
I have tried cout<<"\b \b" and cout<<'\b', but it doesn't work.
You might do
template <class... Ts>
void Fun(Ts... ts)
{
const char* sep = "";
cout << "TS: "; (((cout << sep << ts), sep = ", "), ..., (cout << endl));
}
Demo
or even, as you have header:
template <class... Ts>
void Fun(Ts... ts)
{
const char* sep = "TS: ";
(((cout << sep << ts), sep = ", "), ..., (cout << endl));
}
Demo
Or you could do:
template<class T = const char *, class... Ts>void Fun(T first = "", Ts... ts)
{
cout << "TS: " << first; ((cout << ", " << ts), ..., (cout << endl));
}
I'm implementing a small Log class in c++
class Log
{
public:
enum LogLevel
{
TRACE,
DEBUG,
INFO,
WARNING,
ERROR,
FATAL
};
Log(): m_log(DEBUG) {} ;
~Log() {};
int SetLogFilePath(const std::string& p_directory, const std::string& p_prefix);
void SetLogLevel(const LogLevel p_logLvl);
LogLevel& GetLogLevel();
template<typename... Args>
int Logging(const std::string& p_logLevel, const std::string& p_srcFile, const std::string& p_function, const int& p_line,
const Args&& ... p_args);
private:
LogLevel m_log;
std::string m_logFilePath;
};
This is my Log class, I store directory and prefix into std::string m_logFilePath
template <typename ... Args>
int Log::Logging(const std::string& p_logLevel, const std::string& p_srcFile, const std::string& p_function, const int& p_line,
const Args&&... p_args)
{
std::ofstream log;
log.open(m_logFilePath.c_str(), std::ios::out);
if(!log.good())
{
std::cout << "Couldn't create file : " << m_logFilePath.c_str() << std::endl;
return 0;
}
switch(p_logLevel)
{
case "TRACE":
log << "(TRACE) ";
break;
case "DEBUG":
log << "(DEBUG) ";
break;
case "INFO":
log << "(INFO) ";
break;
case "WARNING":
log << "(WARNING) ";
break;
case "ERROR":
log << "(ERROR) ";
break;
case "FATAL":
log << "(FATAL) ";
break;
default: break;
}
log << "Log Created at (Local Time): " << std::put_time(std::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())), "%c") << " ";
log << "File Name: " << p_srcFile.c_str() << " " << "Function Name: " << p_function.c_str() << " " << "Line: " << p_line << " " << std::forward<Args>(p_args) << std::endl;
log.close();
return 1;
}
Above is my implementation of int Logging(...) function.
Basically, I store the logging level, current time, source file path, function name , line from code, and some variadic arguments.
I've been searching for a way to store multiple variadic arguments into my std::ostream log but couldn't find any answer.
Please Help!
If I understand correctly, your problem is that you don't know how to manage (in this case: send to an output stream) a variadic list (variadic template based) of arguments.
I propose an example of use that use the power of comma operator in the context of an (unused) array initialization
using unused = int[];
int cnt { -1 };
(void)unused { 0, (oss << ", extra val "
<< ++cnt << " [" << args << ']', 0)... };
The following is a simplified, but full working, example
#include <sstream>
#include <iostream>
template <typename ... Args>
std::string logging (std::string const & valA, std::string const & valB,
std::string const & valC, Args const & ... args)
{
std::ostringstream oss;
using unused = int[];
oss << "valA [" << valA << "], "
<< "valB [" << valB << "], "
<< "valC [" << valC << ']';
int cnt { -1 };
(void)unused { 0, (oss << ", extra val "
<< ++cnt << " [" << args << ']', 0)... };
return oss.str();
}
int main()
{
auto l = logging("val1", "val2", "val3", 0, 1L, 2LL, 3U, 4UL, 5ULL,
6.0, 7.0f, "eight");
std::cout << l << std::endl;
}
I am trying to create a general debug print function.
enum class DebugLevel : uint8_t
{
INFO = 0,
EVENT = 1,
WARNING = 2,
ERROR = 3,
CRITICAL = 4
};
DebugLevel generalDebugLevel = DebugLevel::INFO;
template <typename ...T>
void DPRINT (DebugLevel dbgLevel, T&& ...args)
{
if (dbgLevel >= generalDebugLevel)
{
std::cerr << __FILE__ << ":" << __LINE__ << " " << args... << std::endl;
}
}
As you can see, I need to unpack as I pass it to <<.
Any clues?
template <typename ...T>
void DPRINT (DebugLevel dbgLevel, T&& ...args)
{
if (dbgLevel >= generalDebugLevel)
{
std::cerr << __FILE__ << ":" << __LINE__ << " ";
using expander = int[];
(void)expander{0, (void(std::cerr << std::forward<T>(args) << " "),0)...};
std::cerr << std::endl;
}
}