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;
}
}
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)), ...);
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
When compiling this code with -Waddress:
#include <iostream>
#include <memory>
#include <string.h>
template <typename T, void (*func)(T*) = nullptr>
struct Caller {
Caller(T* ptr = nullptr)
{
std::cout
<< "Creating caller " << ptr
<< ", is function " << std::is_function<decltype(func)>()
<< ", is null " << std::is_null_pointer<decltype(func)>()
<< ", function is " << func
<< std::endl;
if (func)
{
std::cout << "Running for " << ptr << " func " << func << std::endl;
func(ptr);
}
}
};
void free_char(char *c) { free(c); }
int main() {
Caller<char, free_char>(strdup("Test"));
Caller<const char>("Test2");
return 0;
}
it will fail with:
/tmp/foo.cpp: In instantiation of ‘Caller<T, func>::Caller(T*) [with T = char; void (* func)(T*) = free_char]’:
/tmp/foo.cpp:36:40: required from here
/tmp/foo.cpp:13:33: warning: the address of ‘void free_char(char*)’ will never be NULL [-Waddress]
A workaround is using something like if (auto f = func) f(ptr); but i'd like to have something that is statically checked at compile time.
This solution mentions the usage of template specialization, but a part that here we're handling a struct, this is a case where i'd like to use static template checks.
How about simply providing a no-op function by default instead of a null pointer? This gets rid of the if altogether and makes the code cleaner in general.
template<typename T>
void no_op(T*) {}
template <typename T, void (*func)(T*) = no_op<T>>
struct Caller {
static_assert(func != nullptr, "don't pass nullptr");
Caller(T* ptr = nullptr)
{
std::cout
<< "Creating caller " << ptr
<< ", is function " << std::is_function<decltype(func)>()
<< ", is null " << std::is_null_pointer<decltype(func)>()
<< ", function is " << func
<< std::endl;
std::cout << "Running for " << ptr << " func " << func << std::endl;
func(ptr);
}
};
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;
}
This code has a partial user specializations for pointers and arrays.
When the array specialization is explicitly called, the expected value is returned.
However, when a variadic template function is used, the array parameter is converted to a pointer, and the pointer specialization is called.
Is there a way to get the compiler (g++ 4.8.1 in this case) to not do that cast?
Or is there a different way to return the "total size" that doesn't use template specializations?
#include <iostream>
template <typename T, typename... Params>
struct TestTemplate
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T>
struct TestTemplate<T>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T);
}
};
template <typename T, typename... Params>
struct TestTemplate<T*, Params...>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T>
struct TestTemplate<T*>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T);
}
};
template <typename T, size_t N, typename... Params>
struct TestTemplate<T[N], Params...>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return N * sizeof (T) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T, size_t N>
struct TestTemplate<T[N]>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return N * sizeof (T);
}
};
template <typename... Params>
size_t GetSizeof (Params... params)
{
return TestTemplate<Params...>::Sizeof();
}
struct TestType
{
double x = 0., y = 0.;
char buf[64];
};
int main (int, char *[])
{
std::cout << TestTemplate<int[10]>::Sizeof() << std::endl; // prints 40. OK
std::cout << GetSizeof (2, 3, 4) << std::endl; // prints 12. OK
TestType tt;
std::cout << GetSizeof (&tt, 1) << std::endl; // prints 84. OK
int int_arr[10];
std::cout << GetSizeof (int_arr, 1) << std::endl; // prints 8, want 41
}
You may replace your GetSizeof by: (https://ideone.com/jqXT4s)
template <typename... Params>
size_t GetSizeof (const Params&... params)
{
return TestTemplate<Params...>::Sizeof();
}
Once you have done that, you may simply use:
template <typename T, typename... Params>
struct TestTemplate
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (typename std::remove_pointer<T>::type) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T>
struct TestTemplate<T>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (typename std::remove_pointer<T>::type);
}
};
as sizeof(T[N]) == N * sizeof(T).