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.
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'm writing a sketch of a simple thread-safe logging library and some things came on my mind. Here's the code:
#ifndef SimpleLogger_H
#define SimpleLogger_H
#include <iostream>
#include <mutex>
class SimpleLogger
{
public:
template <typename T>
static void log(T message)
{
mutex.lock();
std::cout << message;
mutex.unlock();
}
private:
static std::mutex mutex;
}LOG;
template <typename T>
SimpleLogger &operator<<(SimpleLogger &simpleLogger, T message)
{
simpleLogger.log(message);
return simpleLogger;
}
#endif //SimpleLogger_H
My idea is to use it like this:
LOG << "hello" << " world " << 8 << " I can mix integers and strings";
I understand that the line above is like the following:
auto a1 = LOG.operator<<("hello");
auto a2 = a1.operator<<(" world ");
//Another thread may want to use LOG here, and would print in the middle of my message
auto a3 = a2.operator<<(8);
auto a4 = a3.operator<<(" I can mix integers and strings");
As you can see, because << is broken into several funciton calls, there's a risk that a thread can use the LOG object in the middle of my message (I consider a message the entire cascade of << on one line)
Also, is there a way to automatically add an std::endl for the last << call? I couldn't think of a way to do this, but I saw that some logging libraries have this functionality
How do I solve these two problems?
I know it'd be preferable to use a logging library but I want to mix android, desktop and ios logging in one simple lib without need for high performance, and I'm also puzzled by how I can overcome the difficulties I encountered while writing my own
As others already mentioned, you need a local buffer to collect message before sending to the log file. In the example below, SimpleLoggerBuffer objects are designed to be used as temporary variable only. I.e. it gets destroyed at the end of the expression. The destructor flushes the buffer into the log so that you don't have to explicitly call a flush function (you may add endl there as well if you wish)
#include <iostream>
#include <sstream>
#include <mutex>
using namespace std;
class SimpleLogger
{
public:
template <typename T>
static void log(T& message)
{
mutex.lock();
std::cout << message.str();
message.flush();
mutex.unlock();
}
private:
static std::mutex mutex;
}LOG;
std::mutex SimpleLogger::mutex;
struct SimpleLoggerBuffer{
stringstream ss;
SimpleLoggerBuffer() = default;
SimpleLoggerBuffer(const SimpleLoggerBuffer&) = delete;
SimpleLoggerBuffer& operator=(const SimpleLoggerBuffer&) = delete;
SimpleLoggerBuffer& operator=(SimpleLoggerBuffer&&) = delete;
SimpleLoggerBuffer(SimpleLoggerBuffer&& buf): ss(move(buf.ss)) {
}
template <typename T>
SimpleLoggerBuffer& operator<<(T&& message)
{
ss << std::forward<T>(message);
return *this;
}
~SimpleLoggerBuffer() {
LOG.log(ss);
}
};
template <typename T>
SimpleLoggerBuffer operator<<(SimpleLogger &simpleLogger, T&& message)
{
SimpleLoggerBuffer buf;
buf.ss << std::forward<T>(message);
return buf;
}
int main() {
LOG << "hello" << " world " << 8 << " I can mix integers and strings";
}
You could create a helper class that collects all the output and prints on destruction. Outline:
#include <string>
#include <iostream>
struct Msg;
struct Log {
void print(const Msg &m);
};
struct Msg {
std::string m;
Log &l;
Msg(Log &l) : l(l) {}
~Msg() {
// Print the message on destruction
l.print(*this);
}
};
void Log::print(const Msg &m) {
// Logger specific printing... here, append newline
std::cout << m.m << std::endl;
}
Msg &&operator << (Msg &&m, const std::string &s) {
// Append operator
m.m += s;
return std::move(m);
}
// Helper to log on a specific logger. Just creates the initial message
Msg log(Log &l) { return Msg(l); }
int main()
{
Log l;
log(l) << "a" << "b" << "c";
return 0;
}
As the Msg is local, other threads will not interfere with it. Any necessary locking can be done in the Log.print method, which will receive the complete message
A simple solution is to write into files instead of standard output, and specifically, separate file for each thread. That way no locking or any other synchronization is needed. The files can later be merged if lines have parseable format.
Another solution is to write logs asynchronously from a single thread, and initially store the messages in a thread safe (possibly lock free) queue.
Also, is there a way to automatically add an std::endl for the last << call?
Unless I misunderstand, you can simply do stream << message << std::endl.
I think that you can simply use std::clog. It is thread safe and, in contrary of std::cout, designed to output instantly for logging.
From the reference page :
Unless sync_with_stdio(false) has been issued, it is safe to
concurrently access these objects from multiple threads for both
formatted and unformatted output.
I recommend you this Jason Turner's video about cout, clog and cerror.
The simplest approach is to return a temporary proxy from the first << - this can either log your stream for the duration (and unlock on destruction), or simply build a local ostringstream and flush it in a single call (again, on destruction).
Holding a lock while logging isn't great for performance (although it's better than your existing log method, which should use std::lock_guard for exception safety).
Building and discarding a temporary ostringstream is probably better, but if you care about performance you'll need to benchmark, and may well end up requiring something more elaborate (per-thread circular buffers, mmapped files or something).
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
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.
I know it's a simple question, but I'm looking to get my C++ groove on. I would imagine some sort of abort function could be used. But I don't think that'll return any text like die('hello'); would.
There is no "print a message, then exit" in C or C++. You can quite easily write your own function for die (or panic, as I prefer to call it), something like:
void die(const std::string& msg)
{
std::cerr << msg << std::endl;
exit(1);
}
Or, if you want to avoid the problems with creating a string, which may fail under low memory conditons:
void die(const char *msg)
...
(the rest should be the same as above). The drawback here is that you can't trivially concatenate strings, e.g. die(std::string("Could not open file ") + filename); won't work with const char *.
A function that does var_dump is much harder, as there is no direct way to actually fetch the content of a variable of a an arbitrary type, or an array, in C++. You could perhaps do something like this:
template<typename T>
void var_dump(const T& var)
{
std::cout << var << endl;
}
template<typename T, size_t N>
void var_dump(const T (&var)[N])
{
for(i : var)
{
std::cout << i << endl;
}
}
The latter is a bit like this PHP code: foreach($var as $i) echo $i . "\n";. And I may have the syntax slightly wrong, not sure.
I wouldn’t recommend calling std::abort, std::exit or anything like that.
It’s brute-force, and the semantics for proper cleanup are very complicated. You are essentially leaving (at least parts of) your program behind in a very messy state.
A much better way in C++ is to throw an exception which you then catch in main:
#include <stdexcept>
struct fatal_error : std::logic_error {
fatal_error(char const* message) : std::logic_error(message) { }
};
int main() try {
… the normal program flow starts here
} catch (fatal_error const& e) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
Then, instead of die, you’d simply say throw fatal_error("Something went wrong.");.
Assuming you need these equivalents for debugging purpose...
instead of die() you may invoke the debugger;
instead of var_export() you may inspect your variables content with the debugger.