This question already has answers here:
How to make a variadic macro for std::cout?
(4 answers)
Closed 4 years ago.
I read that variable argument functions are no good coding.
I have a very old framework with some variable argument functions I want to remove the variable arguments keeping the debugging function.
DEBUG(wchar_t* text, ...)
This debug function calls a printf-like function using the same string sintaxis %d,%f... etc.
What should be the right approach?
Since you marked the question c++, you could make a new class/function based on c++ streams for instance - perhaps even changing the 'old' system to use your new one. Then over time migrate towards that system and perhaps at some point you could get a rid of the old (the 'DEBUG' in your case).
Surprisingly I would recommend to leave the debugging function as it is, unless you are willing to change the entire interface to the C++ way of IO, aka streams. If you are going to use printf and printf syntax then let it as it is. Otherwise modernize it all the way.
For instance let's stay your implementation is somewhere along the lines:
void dbg(char* txt, ...)
{
va_list args;
va_start(args, txt);
vprintf(txt, args);
va_end(arts);
}
Yes, there are options to get rid of the variadic, but there is 0 gain in doing so if you are going to keep the printf family syntax:
template <class... Args>
auto dbg(const char* fmt, const Args&... args)
{
printf(fmt, args...);
}
Then you realize that char* is more C than C++ and you change to this:
template <class... Args>
auto dbg(const std::string& fmt, const Args&... args)
{
printf(fmt.c_str(), args...);
}
Then you realize that printf is more C than C++ and the option now is to get rid of printf and to scrap this altogether.
This question How to make a variadic macro for std::cout? shows you a way you could do that if you are still set on a function:
template<typename ...Args>
void log(Args && ...args)
{
(std::cout << ... << args);
}
Another option is to make something like this:
log << "this is the " << i << " log";
But it's not trivial to make it add a newline at the end.
In the end, the best solution I think is to use a logging library.
I agree #bolov recommendation to leave the debugging function as it is.
However you may play with std::initializer_list and std::variant (since C++17) types.
Below is a small example that doesn't yet process the format specifiers but can give some ideas to evolve the approach.
#include <iostream>
#include <cstdlib>
#include <variant>
typedef std::variant<std::string, int, float, bool> DebugOutParam;
std::ostream& operator << (std::ostream& os, const DebugOutParam& v)
{
if (std::holds_alternative<std::string>(v))
os << std::get<std::string>(v);
else if (std::holds_alternative<int>(v))
os << std::get<int>(v);
else if (std::holds_alternative<float>(v))
os << std::get<float>(v);
else if (std::holds_alternative<bool>(v))
os << (std::get<bool>(v) ? "true" : "false");
else
os << "?Unsupported?";
return os;
}
typedef std::initializer_list<DebugOutParam> DebugOutParams;
void dbg(std::string fmt, DebugOutParams l)
{
std::cout << fmt << ": ";
for (DebugOutParams::const_iterator it = l.begin(); it != l.end(); it++)
{
DebugOutParam v = *it;
std::cout << (it == l.begin() ? "" : ", ") << v;
}
std::cout << std::endl;
}
int main()
{
dbg("Test", {123, std::string("456"), true, static_cast<float>(456.789)});
}
Output
Test: 123, 456, true, 456.789
Related
I'm using a function like this to export data in a xml file (note: silly example):
void write_xml_file(const std::string& path)
{
using namespace std::string_view_literals; // Use "..."sv
FileWrite f(path);
f<< "<root>\n"sv
<< "\t<nested1>\n"sv
<< "\t\t<nested2>\n"sv
<< "\t\t\t<nested3>\n"sv
<< "\t\t\t\t<nested4>\n"sv;
//...
}
Where those << take a std::string_view argument:
FileWrite& FileWrite::operator<<(const std::string_view s) const noexcept
{
fwrite(s.data(), sizeof(char), s.length(), /* FILE* */ f);
return *this;
}
If necessary I can add an overload with std::string, std::array, ...
Now, I'd really love to write the above like this:
// Create a compile-time "\t\t\t..."sv
consteval std::string_view indent(const std::size_t n) { /* meh? */ }
void write_xml_file(const std::string& path)
{
using namespace std::string_view_literals; // Use "..."sv
FileWrite f(path);
f<< "<root>\n"sv
<< indent(1) << "<nested1>\n"sv
<< indent(2) << "<nested2>\n"sv
<< indent(3) << "<nested3>\n"sv
<< indent(4) << "<nested4>\n"sv;
//...
}
Is there someone that can give me an hint on how implement indent()?
I'm not sure if my idea to return a std::string_view pointing to a static constant buffer allocated at compile time is the most appropriate, I'm open to other suggestions.
If you want indent to work at compile-time, then you will require N to also be a compile time value, or for indent to be called as part of a constexpr sub-expression.
Since this is for the purpose of streaming to some file-backed stream object FileWrite, the latter is out -- which means that you need N to be at compile-time (e.g. pass it as a template argument).
This will change your signature to:
template <std::size_t N>
consteval auto indent() -> std::string_view
The second part of the problem is that you want this to return a std::string_view. The complication here is that constexpr contexts don't allow static variables -- and thus anything you create within the context will have automatic storage duration. Technically speaking, you can't just simply create an array in the function and return a string_view of it -- since this would lead to a dangling pointer (and thus UB) due to the storage going out-of-scope at the end of the function. So you will need to workaround this.
The easiest way is to use a template of a struct that holds a static array (in this case std::array so we can return it from a function):
template<std::size_t N>
struct indent_string_holder
{
// +1 for a null-terminator.
// The '+1' can be removed since it's not _technically_ needed since
// it's a string_view -- but this can be useful for C interop.
static constexpr std::array<char,N+1> value = make_indent_string<N>();
};
This make_indent_string<N>() is now just a simple wrapper that creates a std::array and fills it with tabs:
// Thanks to #Barry's suggestion to use 'fill' rather than
// index_sequence
template <std::size_t N>
consteval auto make_indent_string() -> std::array<char,N+1>
{
auto result = std::array<char,N+1>{};
result.fill('\t');
result.back() = '\0';
return result;
}
And then indent<N> just becomes a wrapper around the holder:
template <std::size_t N>
consteval auto indent() -> std::string_view
{
const auto& str = indent_string_holder<N>::value;
// -1 on the size if we added the null-terminator.
// This could also be just string_view{str.data()} with the
// terminator
return std::string_view{str.data(), str.size() - 1u};
}
We can do a simple test to see if this works at compile-time, which it should:
static_assert(indent<5>() == "\t\t\t\t\t");
Live Example
If you check the assembly, you will also see that indent<5>() produces the correct compile-time string as desired:
indent_string_holder<5ul>::value:
.asciz "\t\t\t\t\t"
Although this works, it would actually probably be much simpler for you to write indent<N>() in terms of FileWrite (or whatever the base class is -- assuming this is ostream) instead of returning a string_view. Unless you are doing buffered writing to these streams, the cost of writing a few single characters should be minimal compared to the cost of flushing the data -- which should make this negligible.
If this is acceptible, then it would actually be much easier since you can now write it as a recursive function that passes \t to your stream object, and then calls indent<N-1>(...), e.g.:
template <std::size_t N>
auto indent(FileWrite& f) -> FileWrite&
{
if constexpr (N > 0) {
f << '\t'; // Output a single tab
return indent<N-1>(f);
}
return f;
}
This changes the use to now be like:
FileWrite f(path);
f<< "<root>\n"sv;
indent<1>(f) << "<nested1>\n"sv;
indent<2>(f) << "<nested2>\n"sv;
indent<3>(f) << "<nested3>\n"sv;
indent<4>(f) << "<nested4>\n"sv;
But the implementation is much easier to grok and understand IMO, compared to producing a string at compile-time.
Realistically, at this point it might just be cleaner to write:
auto indent(FileWrite& f, std::size_t n) -> FileWrite&
{
for (auto i = 0u; i < n; ++i) { f << '\t'; }
return f;
}
which is likely what most people would expect to read; although it does come at the minimal cost of the loop (provided the optimizer does not unroll this).
use of std::string (size_t n, char c);
see also create-string-with-specified-number-of-characters
void write_xml_file(const std::string& path)
{
using namespace std::string_view_literals; // Use "..."sv
FileWrite f(path);
f<< "<root>\n"sv
<< std::string ( 1, '\t') << "<nested1>\n"sv
<< std::string ( 2, '\t') << "<nested2>\n"sv
<< std::string ( 3, '\t') << "<nested3>\n"sv
<< std::string ( 4, '\t') << "<nested4>\n"sv;
//...
}
I used to logging in C with variable amount of arguments and formatting, and I wanna how Can I meet this in C++.
Through Q&A like this (How to make a variadic macro for std::cout?), I know how to handle variable amount. But what I still do not know is, how to format, cause I can not use methods like 'setbase' between arguments now.
For example:
// in C
#define err(fmt, ...) (printf("[%s] "fmt"\n", __FUNCTION__, ##__VA_ARGS__))
#define FATAL(fmt, ...) do{\
err(fmt, ##__VA_ARGS__);\
CLEAN_UP;\
exit(1);\
}while(0)
int main(){
if(1) FATAL("Just a test: 0x%lX, %d", 1, 2);
return 0;
}
"FATAL" here, accept variable amount of arguments with formatting, print them, and do some extra. I have no idea how to declare such a "FATAL" in C++.
You can achieve that by using the operator<< and a custom destructor on an ad-hoc logging object.
class log_error
{
public:
log_error() = default;
log_error(log_error&& other) = default;
~log_error()
{
// Do whatever you want with the input
// Add a timestamp, process/thread id
// Write it to a file, send it to a server ...
std::cerr << "[ERROR] " << ss.str() << std::endl;
throw std::runtime_error(ss.str());
}
std::stringstream ss;
};
template<typename T>
log_error operator<<(log_error&& le, const T& t)
{
le.ss << t;
return std::move(le);
}
I only included the essentials for basic usage. For more complex usage you want to consider a copy variant of the ctor / operator<<.
The usage is very idiomatic C++. But you have to remember the ():
log_error() << "Ooops " << 23 << ", 0x" << std::setbase(16) << 23;
This line will print out the message and throw an exception.
You can customize this however you want. Write to logfiles, add timestamps or other helpful information, verbosity levels and thresholds. It is even possible to have most cases completely optimized out in production builds.
Live example
C++ is not C! While you can use C-style (and often C) code this is not advisable. Firstly you should not normally rely on macros as they violate the type system, use (possibly inlined or constexpr) functions instead. Then you should not use C-style error handling technique, use exceptions instead. I'd also recommend against variadic arguments in general and finally you don't need C-style string formatting techniques -> this is C++, use stringstreams to format your code.
In your particular case I'd do something like this:
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
inline void fatal(std::string msg) {
// clean_up
throw std::runtime_error(msg);
}
int main(){
std::ostringstream msg;
msg << "Just a test: " << 1 << 2;
if(1) fatal(msg.str());
return 0;
}
I also have to point out that C++ and C are two different languages with different patterns and idioms. C++ has better alternatives for many C constructs which are more type-safe and thus preferable. IN your case, I would throw an exception in this case. If you ban catch(...) in your code, it will terminate your program. When the exception is propagated, the compiler will also call destructors of objects and thus do clean-up. If you haven't, I recommend you read up on resource-acquisition-is-initialization (RAII). Since it looks like you are transitioning from C to C++, I recommend to read the tour of C++ which shows fundamental C++ principles. For RAII, the gist is to manage resources in special handler objects which allocate in the constructor and deallocate in the destructor, and implement move semantics. This way, you cannot leak resources. Example implementations are std::vector, std::unique_ptr or std::iostream. As another example, consider mutex locking/unlocking:
class Mutex {
public:
void lock() { ... }
void unlock() { ... }
};
When you use it, it easy to forget unlocking in your code, especially when making modifications to existing code. Also, in case of exceptions, you need try/catch blocks to unlock all the time. Instead, define a MutexLocker class:
class MutexLocker
{
public:
MutexLocker(std::mullptr_t) = delete;
MutexLocker(Mutex* m): mutex_(m) {mutex_->lock();}
MutexLocker(MutexLocker const&) = delete;
MutexLocker& operator=(MutexLocker const&) = delete;
MutexLocker(MutexLocker&& l): mutex_(l.mutex_) {l.mutex_ = nullptr;}
MutexLocker& operator=(MutexLocker&& l)
{
mutex_ = l.mutex_,
l.mutex_ = nullptr;
return *this;
}
~MutexLocker() {if (mutex_) {mutex_->unlock()} };
private:
Mutex* mutex_;
};
Now, you can never forget to unlock a Mutex. The MutexLocker object cannot be copied, but you can transfer ownership. This is superior to anything you can do in C.
For formatting output, you can google "variadic template printf" which should give you some examples, e.g. on Wikipedia:
void printf(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>
void printf(const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
s += 2; // this only works on 2 characters format strings ( %d, %f, etc ). Fails miserably with %5.4f
printf(s, args...); // call even when *s == 0 to detect extra arguments
return;
}
}
std::cout << *s++;
}
}
Or you can use a library, e.g. boost::format or probably thousands of other implementations. If it is only for logging, you could take a look at a logging framework, e.g. boost.log.
First, even it often leads to harder to maintain code, you call always use C techniques in C++. stdio.h functions work natively in C++ and almost all macro are translated the same.
If you want to make use of c++ goodies (better type control at compile time)... you will have to forget old C variadic functions, notably all xprintf. There may be one interesting part anyway with templates.
Anyway the example given in the referenced Q&A is all you need here. Formatting instructions are simply injected in streams the same values are.
But here is a C++11 example showing that you can do what you want without using any macro. It is much longer than the C macro version, but it looks form me much more clear and extensible without the ugly do { ... } while 0 idom:
#include <iostream>
#include <string>
// disp is a variadic templated function injecting any arguments to a stream
// version for one single arg
template <typename T>
void disp(std::ostream& out, T arg) {
out << arg;
}
// recursively displays every arg
template <typename T, typename ... U>
void disp(std::ostream& out, T arg, U ... args) {
disp(out, arg) ;
disp(out, args...);
}
/* fatal displays its args to std::cout, preceded with "FATAL " and followed
* by a newline.
* It then does some cleanup and exits
*/
template<typename ... T>
void fatal(T ... args) {
std::cout << "FATAL ";
disp(std::cout, args...);
std::cout << std::endl;
// cleanup
exit(1);
}
int main() {
int i = 21;
int j = 32;
std::string s = "foo";
if(1) fatal(1, " " , s, " ab ", i, " 0x", std::hex, j);
return 0;
}
output is
FATAL 1 foo ab 21 0x20
Last but not least, you'd better use a throw FatalException() where FatalException is a subclass of std::exception instead of directly use exit(1). You could even write to a stringstream and pass the resulting string to the exception instead of writing to a real stream, but then you should be prepared to deal with bad_alloc exceptions.
Here is a sample design code of what I want to achieve. Basically I wanna store handler functions for different handlerNames and these handler functions can be of variable arguments.
The handler functions should be called on events with the required arguments are passed with Script::Handle(...)
How can I achieve this? Maybe its possible with Variadic Templates?
class Script
{
public:
Script() { /* ... */ }
template<typename TFunction>
void AddHandler(const char *handlerName, TFunction &&function)
{
_handlerMap[handlerName] = std::move(function);
}
void Handle(const char *handlerName, ...)
{
_handlerMap[handlerName](...);
}
private:
typedef std::map<std::string, std::function<void()>> HandlerMapType;
HandlerMapType _handlerMap;
};
//Handler functions
handlerOne() { std::cerr << "One"; }
handlerTwo(std::string a1, int a2) { std::cerr << "Two"; }
handlerThree(bool a1) { std::cerr << "Three"; }
int main(int argc, char **argv)
{
Script script;
script.AddHandler("One", std::bind(&handlerOne));
script.AddHandler("Two", std::bind(&handlerTwo));
script.AddHandler("Three", std::bind(&handlerThree));
script.Handle("One");
script.Handle("Two, "string", 96);
script.Handle("Three", true);
script.Handle("Three", "what should happen here?"); //String passed instead of bool
}
Let me prefix by saying that this is not a trivial thing to do in C++. And I will go as far to say that you should consider whether this is really something you need in your use case. In your example, you are asking for genericism that you can't really use. You will in any case need to know the signature of the function you are calling to call it properly; in that case what purpose is served by putting them in a container?
Generally, you'd do something like this if you are writing a middle layer of code. In your example, this would be equivalent to writing code that enables another user to call Handle. A common concrete example of this is to write a factory where objects in the factory may be instantiated using different arguments. However, it can't really be "different" arguments, at least not without some crazy casting. The solution is to make all the functions take the same argument, but make the argument a dynamic type that can store whatever arguments you want:
using argument_type = std::unordered_map<std::string, boost::any>;
void print(const argument_type & arg) {
auto to_print = boost::any_cast<std::string>(arg["to_print"]);
std::cerr << to_print << std::endl;
}
void print_none(const argument_type & arg) {
std::cerr << "none" << std::endl;
}
using my_func_t = std::function<void(const argument_type &)>;
std::vector<my_func_t> v;
v.emplace_back(print);
v.emplace_back(print_none);
// create some argument_types, feed them to f.
The above is not code that has been tested, nor with a working main, but I think this should give you a sense of how you could accomplish what you want.
edit: I thought about it a bit more, and I decided to elaborate a bit more on the "crazy casting" way. I suppose it's not really more crazy, but I strongly prefer what I showed above. The alternative is to completely type erase the functions themselves, and pass the arguments using a variadic template.
void print(std::string to_print) {
std::cerr << to_print << std::endl;
}
void print_none() {
std::cerr << "none" << std::endl;
}
std::vector<boost::any> v;
v.emplace_back(std::function<void(std::string)>(print));
v.emplace_back(std::function<void(void)>(print_none));
template <typename ... Args>
void call(const std::vector & funcs, int index, Args... args) {
auto f = boost::any_cast<std::function<void(Args...)>>(funcs[index]);
f(std::forward<Args>(args)...);
}
// unsure if this will actually work
call(f, 0, std::string("hello"));
The code above is very fragile though, because the types you pass to call will be deduced against, and then the cast will try to cast to a std::function that matches that signature. That exact signature. I don't have a lot of confidence that this will work out; if it's a reference, vs value, vs rvalue, etc. Casting back to a different std::function than what you put in is undefined behavior.
In summary, I'd either try to avoid needing to do this entirely, or go with the first solution. It's much less fragile, and it's better to be upfront about the fact that you are erasing the signatures of these functions.
EDIT: The code marked as "not working" was actually working. It was because of a syntax problems in my tests, not detected by the compiler. So the question is already solved, thank you.
C++ is not a language I use everyday, so it is possible that the solution is trivial.
About the context first. I use C++ to develop on a microcontroller (Arduino-based, AVR microcontroller), so I do not use the STL, printf-like functions, new/malloc should be avoided and C++ <string> too.
I have an object called Serial similar to the C++ cout iostream, to communicate with the microcontroller with a serial interface. I have overloaded the "<<" operator of the class from which Serial is an instance so I can do something like that:
Serial << "debug " << "value is " << 3 << endl;
// Whithout the << operator it would be:
Serial.print("debug ");
Serial.print("value is ");
Serial.println(3);
I would like to create a function (or a macro) that enables this kind of line only if debugging is enabled, and which automatically add the "debug" string and append the "endl" value at the end.
So something like that (warning, code does not work because "data" cannot expand as a whole C++ instruction):
#ifdef DEBUG
#define PRINT_DEBUG(data) do {Serial << "debug " << data << endl;} while(0)
#else
#define PRINT_DEBUG(data) do {} while(0)
#endif
// This code works
PRINT_DEBUG("hello world");
// This code does not work
int value1 = 3;
char * value2 = "this is a string";
PRINT_DEBUG("sensor1 value:" << value1 << " other sensor value " << value2);
This kind of function/macro would allow me to easily output strings on my serial interface with a specific "string protocol" without having to repeat the "debug" string at the start. It would also allow me to easily disable the print of debug message by not setting the DEBUG macro. I also have only one "#ifdef DEBUG" instead of several ones in my code.
I managed to do something like that with variadic arguments, but I hate this solution because it is dangerous to use (I do not want to specify the number of arguments), and I cannot mix different type of data:
void __rawSend(char * args, ...) {
Serial.print(args);
va_list paramList;
va_start (paramList, args);
while(true) {
char * next = va_arg(paramList, char*);
if (next == NULL) {
break;
}
Serial.print(" ");
Serial.print(next);
}
Serial.println();
va_end(paramList);
}
#ifdef DEBUG
#define printDebug(...) do {__rawSend(OUTPUT_DEBUG, __VA_ARGS__, NULL);} while(0)
#else
#define printDebug(...) do {} while(0)
#endif
int intValue = 1;
char * stringValue = "data";
// This works
printDebug("hello",stringValue);
// This does not works
printDebug("data is", intValue);
How can I do that? Is it possible with macros (while avoiding variadic arguments and mixing different kind of types)? Is there a better solution?
Sorry all, the code marked as "not working" does actually work. It was because of a syntax problems in my tests, not detected by the compiler.
Anyway, my solution can benefit other people working with Arduino, as I have seen solutions using printf or trying to recreate printf.
I used the "<<" operator coming from http://arduiniana.org/libraries/streaming/
I tend to avoid macros for this sort of things and use classes and static polymoprhism instead :
// Define different types providing a stream interface
struct DebugStream
{
template <typename T>
std::ostream & operator<< (const T & x) const {
return Serial << "debug " << x;
}
// This one is for stream manipulators
std::ostream & operator<< (std::ostream& (*x) (std::ostream&)) const {
return Serial << "debug " << x;
}
};
// This type also provides a stream-like interface but does nothing
struct NoStream
{
template <class T>
const NoStream & operator<< (const T & x) const {
return *this;
}
const NoStream & operator<< (std::ostream& (*x) (std::ostream&)) const {
return *this;
}
};
// Instanciate a debug object having one of the previously defined types
//
// Make sure to declare debug in a common .hxx file included everywhere else
// but to define it only once in a .cxx file.
#ifdef DEBUG
DebugStream debug;
#else
NoStream debug;
#endif
// Use it like you would use the Serial iostream
debug << "value is " << 3 << std::endl;
Since everything is inlined and exact types are known at compile-time, the compiler can optimize out all unnecessary operations on NoStream instances.
If I understand your problems correctly...
Looks to me like you need to overload operator<< for all types you're going to send to the debug interface.
The var args macro has to have a way to deduce the types of its arguments. The way you have it implemented it's expecting all C type strings. You'd be better off with printf or a library like fastformat.
If your operator<< is not returning a reference to the class that allows operator<< to be chained, you'll get errors like "I have the following error for the line "DEBUG("hello" << " " << "world");" : invalid operands of types 'const char [6]' and 'const char [2]' to binary 'operator<<' ". I do not believe DEBUG("hello" << " " << "world"); can be made to work. DEBUG( "hello", "world"); might work.
I want to make a custom print function that takes any amount of arguments and prints them all on new lines.
In javascript, the document.write and console.log functions can do this because javascript stores all arguments in an array. To my knowledge, c++ doesn't do this and can't because of type restrictions.
So is there a proper way to do this in c++? Take any amount of arguments, regardless of type, and print them all?
Yes there is with the new C+11 standard. Please take a look at this article :
http://en.wikipedia.org/wiki/Variadic_template
There is a pretty nice example also you can use as a starter.
You can use a variadic function, similar to printf, but you will still need to know the types of the arguments and how many there are to be able to access them properly.
A better approach would probably be to do something similar to how the << operator was overloaded for ostream or use a chainable function. You will in the end still be limited to types (or supertypes with virtual functions) you know about.
From Bjarne himself:
http://www2.research.att.com/~bs/C++0xFAQ.html#variadic-templates
His code as of today:
void printf(const char* s)
{
while (s && *s) {
if (*s=='%' && *++s!='%') // make sure that there wasn't meant to be more arguments
// %% represents plain % in a format string
throw runtime_error("invalid format: missing arguments");
std::cout << *s++;
}
}
template<typename T, typename... Args> // note the "..."
void printf(const char* s, T value, Args... args) // note the "..."
{
while (s && *s) {
if (*s=='%' && *++s!='%') { // a format specifier (ignore which one it is)
std::cout << value; // use first non-format argument
return printf(++s, args...); // "peel off" first argument
}
std::cout << *s++;
}
throw std::runtime error("extra arguments provided to printf");
}