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;
}
Related
I've a class withmap<string,vector<string>>.
I want to give a user a member function to receive the value for a key in different formats than std::vector(string):
vector(string),
string,
vector(int),
vector(float) and
bool
example:
bool x = (bool) myClass["dummy_boolean_type"]
x = myClass["dummy_boolean_type"].as<bool>
int y = (int) myClass["dummy_boolean_type"]
Can someone have an example what is the best way to achieve it ?
(without a use in Boost)
You cannot provide your class with any member function or functions
that will support the two constructions you want:
T x = myClassInstance["key"].as<T> // (1)
T x = (T) myClassInstance["key"] // (2)
In the case of (1), the immediate reason is simply that the construction is
not legal C++. So let's suppose it is replaced by:
T x = myClassInstance["key"].as<T>() // (1a)
But this doesn't help, and the reason is the same for both (1a) and (2):
The expression myClassInstance["key"] will have to evaluate to some object e or reference to such that is returned by:
myClass::operator[](std::string const &);
This e is the vector<string> to which key maps in your
map<string,vector<string> data member of myClass. It is not myClassInstance.
So what you are asking for are member functions of myClass that will support
the constructions:
T x = e.as<T> // (1b)
T x = (T) e // (2b)
where e is an std::vector<std::string>.
Clearly, nothing you can do in myClass can address your requirement. In
(1b) and (2b), myClass is nowhere to be seen.
Is there any template member function of std::vector<std::string>:
template<typename T>
T as() const;
that has the behaviour you want for (1b)?
Does std::vector<std::string> have any template member function:
template<typename T>
operator T() const;
that has the behaviour you want for (2b)?
No and No.
However...
You can implement an as template member function of myClass that
supports conversions such as you mention. Its signature - of course -
would be:
template<typename T>
T myClass::as(std::string const & key) const;
and you would invoke it like:
T x = myClassInstance.as<T>("key") // (1c)
I'll sketch an implemention that assumes we're content with the
conversions from std::vector<std::string> to:
std::string
std::vector<int>
bool
which will be enough to get you under way.
To convert an std::vector<std::string> vs to std::string I'll concatenate
the elements of vs.
To convert an std::vector<std::string> vs to std::vector<int> I'll convert each element of vs from a decimal numeral, if I can, to the
integer the numeral represents, and return a vector of those integers. Without
prejudice to other policies I'll throw an std::invalid_argument exception
when an element doesn't trim to a decimal numeral.
I am spoilt for choice as to what you might mean by converting an std::vector<std::string>
to bool, so I will arbitrarily say that vs is true if I can convert it
to a vector<int> of which any element is non-0 and is false if I can convert
it to a vector<int> in which all elements are 0.
The sketch:
#include <type_traits>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
namespace detail {
template<typename T>
struct convert_to
{
static T from(std::vector<std::string> const & vs) {
static constexpr bool always_false = !std::is_same<T,T>::value;
static_assert(always_false,
"Calling `convert_to<T>::from` for unimplemented `T`");
return *(T*)nullptr;
}
};
template<>
std::string convert_to<std::string>::from(std::vector<std::string> const & vs)
{
std::string s;
for ( auto const & e : vs ) {
s += e;
}
return s;
}
template<>
std::vector<int>
convert_to<std::vector<int>>::from(std::vector<std::string> const & vs)
{
auto lamb = [](std::string const & s) {
std::size_t lastoff = s.find_last_not_of(" \t\f\v\n\r");
int i;
try {
std::size_t nlen;
i = std::stoi(s,&nlen);
if (nlen <= lastoff) {
throw std::invalid_argument("");
}
}
catch(std::invalid_argument const & e) {
throw std::invalid_argument(
"Cannot convert \"" + s + "\" to int");
}
return i;
};
std::vector<int> vi;
std::transform(vs.begin(),vs.end(),std::back_inserter(vi),lamb);
return vi;
}
template<>
bool convert_to<bool>::from(std::vector<std::string> const & vs)
{
auto vi = convert_to<std::vector<int>>::from(vs);
for (auto const & i : vi) {
if (i) {
return true;
}
}
return false;
}
} // namespace detail
struct myClass // Your class
{
// Whatever...
std::vector<std::string> & operator[](std::string const & key) {
return _map[key];
}
template<typename T>
T as(std::string const & key) {
return detail::convert_to<T>::from(_map[key]);
}
// Whatever...
private:
std::map<std::string,std::vector<std::string>> _map;
};
The one take-away point here is the use of template<typename T>
detail::struct convert_to, with its solitary static member function:
T from(std::vector<std::string> const & vs)
which in the default instantiation will provoke a static_assert failure
reporting that no conversion to T from std::vector<std::string> has been
defined.
Then, for each type U to which you want a conversion, you have just to
write a specializing definition:
template<>
U convert_to<U>::from(std::vector<std::string> const & vs);
as you see fit, and the construction (1c) will use it as per:
template<typename T>
T myClass::as(std::string const & key) {
return detail::convert_to<T>::from(_map[key]);
}
Here's an illustrative progam you can append to the sketch:
#include <iostream>
using namespace std;
template<typename T>
static void print_vec(std::vector<T> const & v)
{
cout << "{ ";
for (auto const & e : v) {
cout << e << " ";
}
cout << "}\n";
}
static void print_vec(std::vector<std::string> const & v)
{
cout << "{ ";
for (auto const & e : v) {
cout << '\"' << e << "\" ";
}
cout << "}\n";
}
int main()
{
myClass f;
f["int_vec"] = vector<string>{"0","1 "," 2"};
cout << "f[\"int_vec\"] = "; print_vec(f["int_vec"]);
f["true_vec"] = vector<string>{"0"," 1 ","0"};
cout << "f[\"true_vec\"] = "; print_vec(f["true_vec"]);
f["false_vec"] = vector<string>{"0"," 0","0 "};
cout << "f[\"false_vec\"] = "; print_vec(f["false_vec"]);
f["not_int_vec0"] = vector<string>{"0","1","2",""};
cout << "f[\"not_int_vec0\"] = "; print_vec(f["not_int_vec0"]);
f["not_int_vec1"] = vector<string>{"0","#","2",};
cout << "f[\"not_int_vec1\"] = "; print_vec(f["not_int_vec1"]);
f["not_int_vec2"] = vector<string>{"0"," 1$","2",};
cout << "f[\"not_int_vec2\"] = "; print_vec(f["not_int_vec2"]);
cout << "f.as<string>(\"int_vec\") = \""
<< f.as<string>("int_vec") << '\"' << endl;
cout << "f.as<string>(\"true_vec\") = \""
<< f.as<string>("true_vec") << '\"' << endl;
cout << "f.as<string>(\"false_vec\") = \""
<< f.as<string>("false_vec") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec0\") = \""
<< f.as<string>("not_int_vec0") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec1\") = \""
<< f.as<string>("not_int_vec1") << '\"' << endl;
cout << "f.as<string>(\"not_int_vec2\") = \""
<< f.as<string>("not_int_vec2") << '\"' << endl;
vector<int> va = f.as<vector<int>>("int_vec");
cout << "f.as<vector<int>>(\"int_vec\") = ";
print_vec(f.as<vector<int>>("int_vec"));
cout << boolalpha << "f.as<bool>(\"true_vec\") = "
<< f.as<bool>("true_vec") << endl;
cout << boolalpha << "f.as<bool>(\"false_vec\") = "
<< f.as<bool>("false_vec") << endl;
try {
cout << "f.as<vector<int>>(\"not_int_vec0\")...";
auto b = f.as<vector<int>>("not_int_vec0");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
try {
cout << "f.as<vector<int>>(\"not_int_vec1\")...";
auto b = f.as<vector<int>>("not_int_vec1");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
try {
cout << "f.as<vector<int>>(\"not_int_vec2\")...";
auto b = f.as<vector<int>>("not_int_vec2");
(void)b;
}
catch(std::invalid_argument const & e) {
cout << e.what() << endl;
}
// char ch = f.as<char>("int_vec"); <- static_assert fails
return 0;
}
It outputs:
f["int_vec"] = { "0" "1 " " 2" }
f["true_vec"] = { "0" " 1 " "0" }
f["false_vec"] = { "0" " 0" "0 " }
f["not_int_vec0"] = { "0" "1" "2" "" }
f["not_int_vec1"] = { "0" "#" "2" }
f["not_int_vec2"] = { "0" " 1$" "2" }
f.as<string>("int_vec") = "01 2"
f.as<string>("true_vec") = "0 1 0"
f.as<string>("false_vec") = "0 00 "
f.as<string>("not_int_vec0") = "012"
f.as<string>("not_int_vec1") = "0#2"
f.as<string>("not_int_vec2") = "0 1$2"
f.as<vector<int>>("int_vec") = { 0 1 2 }
f.as<bool>("true_vec") = true
f.as<bool>("false_vec") = false
f.as<vector<int>>("not_int_vec0")...Cannot convert "" to int
f.as<vector<int>>("not_int_vec1")...Cannot convert "#" to int
f.as<vector<int>>("not_int_vec2")...Cannot convert " 1$" to int
(gcc 5.1, clang 3.6, C++11)
I would like to create a class for logging purposes which will behave like std::cout, but will automatically insert additional information to the stream.
a sample usage that I want would be something like (lets not care about object and context type for now, just assume they are std::string) :
Logger l;
l << "Event with object : " << obj << " while in context : " << context;
Then the output would be :
[timestamp] Event with object : [obj_desc] while in context : [this context][eol][flush]
I've been trying with :
template<typename T>
Logger& operator << (const T& msg){
std::cout << timestamp() << msg << std::endl << std::flush;
return *this;
}
but it seems that std::cout cannot resolve the typename T and fails to output a std::string for example while segfaulting.
A possible solution would be to overload this for all types, but this is rather annoying and time consuming.
Is there a better option to decorate std::cout output with more information?
Edit :
I do realize now that endl and flush will be appended to every message which kind of defeat the purpose, but I'm still interested in the general idea. I care more about the monadic syntax to append an arbitrary number of messages than the <<overload
The reason your code does not work is because you have not implemented operator<< for everything you want to pass to it.
This statement:
Logger l;
l << "Event with object : " << obj << " while in context : " << context;
Is basically doing this (assuming operator<< is a member of Logger, which your implementation implies it is):
Logger l;
l.operator<<("Event with object : ").operator<<(obj).operaator<<(" while in context : ").operator<<(context);
So, you need separate overloads of operator<< for string, obj, context, etc. And you need a way to indicate when to flush the complete log message to std::cout.
I would suggest something more like this:
struct LoggerStream
{
std::ostringstream strm;
struct Timestamp
{
};
~LoggerStream()
{
std::string s = strm.str();
if (!s.empty())
std::cout << s << std::flush;
}
LoggerStream& operator<< (const Timestamp &t)
{
strm << "[timestamp] "; // format this however you need
return *this;
}
LoggerStream& operator<< (const object &obj)
{
strm << "[obj_desc]"; // format this however you need
return *this;
}
LoggerStream& operator<< (const context &ctx)
{
strm << "[this context]"; // format this however you need
return *this;
}
LoggerStream& operator<< (std::ostream&(*f)(std::ostream&))
{
if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush)
{
std::string s = strm.str();
if (!s.empty())
std::cout << s << std::flush;
strm.str("");
strm.clear();
}
else
strm << f;
return *this;
}
template<typename T>
LoggerStream& operator<< (const T& value)
{
strm << value;
return *this;
}
};
class Logger
{
LoggerStream getStream()
{
LoggerStream strm;
strm << Timestamp;
return strm;
}
};
Then you can do things like this:
Logger l;
l.getStream() << "Event with object : " << obj << " while in context : " << context;
...
l.getStream() << "Event with object : " << obj << " while in context : " << context;
...
Logger l;
LoggerStream strm = l.getStream();
strm << "Event with object : " << obj << " while in context : " << context << std::flush;
...
strm << Logger::Timestamp << "Event with object : " << obj << " while in context : " << context << std::flush;
...
Alternatively:
struct Logger
{
std::ostringstream strm;
~Logger()
{
std::string s = strm.str();
if (!s.empty())
std::cout << "[timestamp] " << s << std::flush;
}
Logger& operator<< (const object &obj)
{
strm << "[obj_desc]"; // format this however you need
return *this;
}
Logger& operator<< (const context &ctx)
{
strm << "[this context]"; // format this however you need
return *this;
}
Logger& operator<< (std::ostream&(*f)(std::ostream&))
{
if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush)
{
std::string s = strm.str();
if (!s.empty())
std::cout << "[timestamp] " << s << std::flush;
strm.str("");
strm.clear();
}
else
strm << f;
return *this;
}
template<typename T>
Logger& operator<< (const T& value)
{
strm << value;
return *this;
}
};
Logger() << "Event with object : " << obj << " while in context : " << context;
...
Logger() << "Event with object : " << obj << " while in context : " << context;
...
Logger l;
l << "Event with object : " << obj << " while in context : " << context << std::flush;
...
l << "Event with object : " << obj << " while in context : " << context << std::flush;
...
You can certainly overload the stream classes if you want, providing operator<< for all the data types you want to support (and that's probably the "correct" way to go) but, if all you're after is a quick way to add logging to a regular stream, there a simpler way:
#include <iostream>
#include <iomanip>
#include <sstream>
#include <ctime>
#include <unistd.h>
#define logcout std::cout << timestamp()
std::string timestamp(void) {
time_t now = time(0);
struct tm *tmx = localtime(&now);
std::ostringstream oss;
oss << '['
<< (tmx->tm_year+1900)
<< '-'
<< std::setfill('0') << std::setw(2) << (tmx->tm_mon+1)
<< '-'
<< std::setfill('0') << std::setw(2) << (tmx->tm_mday)
<< ' '
<< std::setfill('0') << std::setw(2) << (tmx->tm_hour)
<< ':'
<< std::setfill('0') << std::setw(2) << (tmx->tm_min)
<< ':'
<< std::setfill('0') << std::setw(2) << (tmx->tm_sec)
<< "] ";
return oss.str();
}
int main (int argc, char *argv[]) {
logcout << "A slightly\n";
sleep (5);
logcout << "sneaky" << " solution\n";
return 0;
}
which outputs:
[2015-05-26 13:37:04] A slightly
[2015-05-26 13:37:09] sneaky solution
Don't be fooled by the size of the code, I just provided a complete compilable sample for testing. The crux of the matter is the single line:
#define logcout std::cout << timestamp()
where you can then use logcout instead of std::cout, and every occurrence prefixes the stream contents with an arbitrary string (the timestamp in this case, which accounts for the bulk of the code).
It's not what I would call the most pure C++ code but, if your needs are basically what you stated, it'll certainly do the trick.
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;
}
}
Is there any kind of XML parser wrapper library that would allow switching the actual XML parser engine at configuration or run time instead of forcing me to choose between libxml2, expat or Xalan-C++?
I wrote something similar a while back:
struct xerces;
struct msxml;
struct rapid;
struct tiny;
struct pugixml;
template <typename T> struct platform_manager;
template <typename T> double parse_file(std::string const& f, QueryPerfCounter& qpc);
template<class T>
void demo(std::string const& f, size_t N = 10) {
platform_manager<T> pm;
QueryPerfCounter qpc;
std::vector<double> timing_data;
timing_data.reserve(N);
std::generate_n(std::back_inserter(timing_data), N, std::tr1::bind(&parse_file<typename T>, f, qpc));
adobe::Statistics<double> s(timing_data.begin(), timing_data.end());
std::cout << "Iteration count: " << s.count() << " Mean time: " << s.mean() << "s. Variance: " << s.variance() << "s.\n";
}
/***************************************************************/
template <>
struct platform_manager<msxml> {
platform_manager() {
if (FAILED(CoInitialize(NULL)))
throw std::runtime_error("CoCreateInstance failed");
}
~platform_manager() {
CoUninitialize();
}
};
template<>
double parse_file<msxml>(std::string const& f, QueryPerfCounter& qpc) {
CComPtr<IXMLDOMDocument> pXMLDom;
HRESULT hr = CoCreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pXMLDom));
CComPtr<IXMLDOMParseError> pXMLErr;
VARIANT_BOOL varStatus;
qpc.Start();
if (FAILED(pXMLDom->load(CComVariant(f.c_str()), &varStatus)))
std::cout << "Parsing failed" << std::endl;
qpc.Stop();
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#ifdef XERCES_CPP_NAMESPACE_USE
XERCES_CPP_NAMESPACE_USE
#endif
template <>
struct platform_manager<xerces> {
platform_manager() try {
XMLPlatformUtils::Initialize();
} catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Failed to init: " << XMLString::transcode(message) << std::endl;
XMLString::release(&message);
}
~platform_manager() {
XMLPlatformUtils::Terminate();
}
};
template<>
double parse_file<xerces>(std::string const& f, QueryPerfCounter& qpc) {
double duration = 0;
std::tr1::shared_ptr<XercesDOMParser> parser(new XercesDOMParser());
parser->setValidationScheme(XercesDOMParser::Val_Always);
parser->setDoNamespaces(true); // optional
std::tr1::shared_ptr<ErrorHandler> errHandler(new HandlerBase());
parser->setErrorHandler(errHandler.get());
try {
qpc.Start();
parser->parse(f.c_str());
qpc.Stop();
duration = qpc.Duration(QueryPerfCounter::seconds);
}
catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
}
catch (const DOMException& toCatch) {
char* message = XMLString::transcode(toCatch.msg);
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
}
catch (...) {
std::cout << "Unexpected Exception \n" ;
}
return duration;
}
/***************************************************************/
#include "rapidxml.hpp"
#include <vector>
#include <fstream>
#include <iterator>
template <>
struct platform_manager<rapid> {};
enum size_hint { B = 1, KB = 1024, MB = 1024 * 1024 };
double file_size(std::ifstream& f, size_hint factor = MB) {
f.seekg (0, std::ios::end);
size_t length = f.tellg();
f.seekg (0, std::ios::beg);
return double(length) / factor;
}
template<>
double parse_file<rapid>(std::string const& f, QueryPerfCounter& qpc) {
double duration = 0;
rapidxml::xml_document<> doc;
try {
qpc.Start();
std::ifstream myfile(f.c_str());
myfile.seekg (0, std::ios::end);
size_t length = myfile.tellg();
myfile.seekg (0, std::ios::beg);
std::vector<char> buffer(length);
myfile.read(& buffer[0], length);
//buffer.reserve(length);
//buffer.insert(std::istreambuf_iterator<char>(myfile)), std::istreambuf_iterator<char>( ));
//std::copy(std::istreambuf_iterator<char>(myfile), std::istreambuf_iterator<char>( ), std::back_insert_iterator(buffer));
buffer.push_back('\0');
qpc.Stop();
duration += qpc.Duration(QueryPerfCounter::seconds);
//std::cout << "Buffer load time: " << duration << "s" << std::endl;
//QueryPerfCounter qpc;
qpc.Start();
doc.parse<rapidxml::parse_non_destructive>(&buffer[0]);
qpc.Stop();
duration += qpc.Duration(QueryPerfCounter::seconds);
} catch (rapidxml::parse_error const& e) {
std::cout << e.what() << std::endl;
} catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
return duration;
}
/***************************************************************/
template <>
struct platform_manager<tiny> {};
template<>
double parse_file<tiny>(std::string const& f, QueryPerfCounter& qpc) {
tinyxml2::XMLDocument doc;
qpc.Start();
doc.LoadFile(f.c_str());
doc.PrintError(); // emits nothing on success
qpc.Stop();
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
struct banner_printer {
banner_printer(std::string const& libname, std::string const& input) : lib(libname), in(input) {
std::cout << "/*+------------------- BEGIN test for " << lib << " with file: " << in << " -------------------+*/" << std::endl;
}
~banner_printer() {
std::cout << "/*+------------------- END test for " << lib << " with file: " << in << " -------------------+*/" << std::endl;
}
private:
std::string lib, in;
};
/***************************************************************/
#include "pugixml.hpp"
template <>
struct platform_manager<pugixml> {};
template<>
double parse_file<pugixml>(std::string const& f, QueryPerfCounter& qpc) {
pugi::xml_document doc;
qpc.Start();
pugi::xml_parse_result result = doc.load_file(f.c_str());
qpc.Stop();
if (!result) {
std::cout << "XML [" << f << "] parsed with errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n";
std::cout << "Error description: " << result.description() << "\n";
std::cout << "Error offset: " << result.offset << " (error at offset [..." << (result.offset) << "]\n\n";
}
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
int main() {
std::vector<std::string> v = parse_catalog("D:/Work/xml_parsers/perfcompare/benchmark/catalog.txt");
std::for_each(v.begin(), v.end(), [](std::string const& s) {
{
std::ifstream f(s);
std::cout << "Input file name: " << s << " size: " << file_size(f) << "MB\n\n";
}
{
banner_printer b("xerces", s);
demo<xerces>(s);
}
{
banner_printer b("rapid", s);
demo<rapid>(s);
}
{
banner_printer b("tiny", s);
demo<tiny>(s);
}
{
banner_printer b("pugi", s);
demo<pugixml>(s);
}
{
banner_printer b("MSXML6", s);
demo<msxml>(s);
}
}
);
//expat_demo(argc, argv);
return 0;
}
It may or may not help you get started. I've skipped header includes and some other trivia. I tried to keep the interface simple and identical. This meant that some libraries required additional helper functions.
Is there are a better way than this one to print 2d table?
std::cout
<< std::setw(25) << left << "FF.name"
<< std::setw(25) << left << "BB.name"
<< std::setw(12) << left << "sw.cycles"
<< std::setw(12) << left << "hw.cycles" << "\n"
<< std::setw(25) << left << "------"
<< std::setw(25) << left << "------"
<< std::setw(12) << left << "---------"
<< std::setw(12) << left << "---------" << "\n";
You could put the headers into an array or vector, then generate the correct widths automatically:
boost::array<std::string, 4> head = { ... }
BOOST_FOREACH(std::string& s, head)
{
int w = 5*(s.length()/5 + 1);
std::cout << std::setw(w) << left << s;
}
std::cout << '\n';
BOOST_FOREACH(std::string& s, head)
{
int w = 5*(s.length()/5 + 1);
std::cout << std::string(w,'-');
}
std::cout << std::endl;
Might be worthwhile if you have lots of headers I guess.
Use printf. It's part of C, but it's still supported in C++.
Break things up into functions and it's not so bad.
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
typedef std::vector< std::string > StringVector;
void printRow( std::ostream& s, const StringVector& strings )
{
s << std::setw(25) << std::left << strings[ 0 ];
s << std::setw(25) << std::left << strings[ 1 ];
s << std::setw(12) << std::left << strings[ 2 ];
s << std::setw(12) << std::left << strings[ 3 ];
s << "\n";
}
void printHeadings( std::ostream& s, const StringVector& headings )
{
printRow( s, headings );
StringVector lines;
for ( StringVector::const_iterator H = headings.begin();
H != headings.end();
++H )
{
lines.push_back( std::string( H->size(), '-' ) );
}
printRow( s, lines );
}
int main()
{
StringVector headings( 4 );
headings[ 0 ] = "FF.name";
headings[ 1 ] = "BB.name";
headings[ 2 ] = "sw.cycles";
headings[ 3 ] = "hw.cycles";
printHeadings( std::cout, headings );
StringVector contents( 4 );
contents[ 0 ] = "foo";
contents[ 1 ] = "bar";
contents[ 2 ] = "baz";
contents[ 3 ] = "foobarbaz";
printRow( std::cout, contents );
return 0;
}
Note that I've left out the bounds checking on the string vectors since this is only intended as an example.
Width is the only formatting setting that gets reset, so you can trivially simplify to:
std::cout << left;
std::cout
<< std::setw(25) << "FF.name"
<< std::setw(25) << "BB.name"
<< std::setw(12) << "sw.cycles"
<< std::setw(12) << "hw.cycles"
<< "\n";
You might like:
//...
std::cout << w(25, "FF.name");
//...
// implemented:
template<class T>
struct AtWidth {
std::streamsize width;
T const& obj;
AtWidth(std::streamsize width, T const& obj) : width(width), obj(obj) {}
template<class Stream> // easier than templating on basic_stream
friend Stream& operator<<(Stream& s, AtWidth const& value) {
s.width(value.width);
return s << value.obj;
}
};
template<class T>
AtWidth<T> w(std::streamsize width, T const& obj) {
return AtWidth<T>(width, obj);
}
Or if you want to rename the setw function (C++0x):
auto w(std::streamsize width) -> decltype(std::setw(width)) {
return std::setw(width);
}
// ...
std::cout << w(25) << "FF.name";
(In non-0x, you'd have to either know how your library implements setw to get the right type or re-write it yourself, which isn't too hard, BTW.)
Edit: I missed the second line of dashes here, another answer covers that well enough. The solution for that is indeed to construct a header "object" and print it out more or less at once.