How does QMessageLogger magic work? - c++

I am working on a logger framework for QT applications. I am not using QMessageLogger directly because of understanding and learning purposes. There is one thing about one QMessageLogger functionality that I would really like to have in my logger but I dont know how does it work. Lets take for example the qDebug macro:
#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug
One can call this function in 2 ways:
1st way:
qDebug("abc = %u", abc);
2nd way:
qDebug() << "abc = " << abc;
I am looking at the library code, but I cannot quite understand how is it implemented that one can work with QMessageLogger by using va_args as well as some stream object.
How can I achieve such effect? I would really appreciate all help, would be grateful for an example.
Here is my print method body. I need to achieve simmilar functionality with the "stream" way:
/*!
* \brief Adds the log line to the print queue.
* \param lvl: Log level of the line.
* \param text: Formatted input for va_list.
*/
void CBcLogger::print(MLL::ELogLevel lvl, const char* text, ...)
{
// check if logger initialized
if (!m_loggerStarted)
return;
// check if log level sufficient
if (lvl > m_setLogLvl)
return;
logLine_t logline;
logline.loglvl = lvl;
logline.datetime = QDateTime::currentDateTime();
va_list argptr;
va_start(argptr, text);
char* output = NULL;
if (vasprintf(&output, text, argptr))
{
logline.logstr = output;
delete output;
}
va_end(argptr);
emit addNewLogLine(logline);
}

First, you need to understand what is the following
QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug
The above line constructs a QMessageLogger instance and immediately accesses its debug member. Since it is a macro, it's also important what you write in code right after it.
If you look at what QMessageLogger::debug is, you'll see four overloads, and the first two of them are pertinent to your question:
void debug(const char *msg, ...) const Q_ATTRIBUTE_FORMAT_PRINTF(2, 3);
QDebug debug() const;
QDebug debug(const QLoggingCategory &cat) const;
QDebug debug(CategoryFunction catFunc) const;
Now the matter should be simple. If you call qDebug("abc = %u", abc), you're calling the first overload, and the expanded macro is as follows:
QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug("abc = %u", abc)
which is more or less equal to
QMessageLogger temp(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC);
temp.debug("abc = %u", abc);
In the second case you're calling an overload that returns a QDebug object. QDebug has overloaded operator<<. The expanded macro is as follows:
QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug() << "abc = " << abc;
which is more or less equal to
QMessageLogger temp(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC);
QDebug anotherTemp = temp.debug();
anotherTemp << "abc = " << abc;
Here's a simple implementation of such logger:
void addNewLogLine(char const* ptr){
cout << "addNewLogLine: " << ptr << endl;
}
struct LoggerHelper
{
std::stringstream s;
explicit LoggerHelper()=default;
LoggerHelper(LoggerHelper&&) = default;
~LoggerHelper(){
auto str = s.str();
addNewLogLine(str.c_str());
}
template<typename T>
LoggerHelper& operator<<(T const& val){
s << val;
return *this;
}
};
struct Logger
{
void operator()(char const* fmt, ...) const {
char* buf;
va_list args;
va_start(args, fmt);
vasprintf(&buf, fmt, args);
va_end(args);
addNewLogLine(buf);
free(buf);
}
LoggerHelper operator()() const {
return LoggerHelper{};
}
};
demo
Several notes:
I adhered to your interface, but personally, I'd use variadic templates instead of va_args
you're supposed to free the buffer returned by vasprintf. free is not interchangeable with delete or delete[]
I used std::stringstream, but changing it to QTextStream or any other should be simple enough
You don't need to implement helper as a separate class if you're okay with allowing log << "foo" << "bar" syntax as opposed to log() << "foo" << "bar"

Related

Creating a generic conversion function

I have a ResourceManager which takes in classes of type Resource. Resource is a parent class of other classes such as ShaderProgram, Texture, Mesh and even Camera who are completely unrelated to one another.
Suffice it to say, the ResourceManager works. But there is one thing that is very tedious and annoying, and that's when I retrieve the objects from the ResourceManager. Here is the problem:
In order to get an object from ResourceManager you call either of these functions:
static Resource* get(int id);
static Resource* get(const std::string &name);
The first function checks one std::unordered_map by an integer id; whereas the second function checks another std::unordered_map by the name that is manually given by the client. I have two versions of these functions for flexibility sakes because there are times where we don't care what the object contained within ResourceManager is (like Mesh) and there are times where we do care about what it is (like Camera or ShaderProgram) because we may want to retrieve the said objects by name rather than id.
Either way, both functions return a pointer to a Resource. When you call the function, it's as easy as something like:
rm::get("skyboxShader");
Where rm is just a typedef of ResourceManager since the class is static (all members/functions are static). The problem though is that the rm::get(..) function returns a Resource*, and not the child class that was added to the ResourceManager to begin with. So, in order to solve this problem I have to do a manual type conversion so that I can get ShaderProgram* instead of Resource*. I do it like this:
auto s = static_cast<ShaderProgram*>(rm::get(name));
So, everytime I want to access a Resource I have to insert the type I want to actually get into the static_cast. This is problematic insofar that everytime someone needs to access a Resource they have to type convert it. So, naturally I created a function, and being that ShaderProgram is the subject here, thus:
ShaderProgram* Renderer::program(const std::string &name)
{
auto s = static_cast<ShaderProgram*>(rm::get(name));
return s;
}
This function is static, and ResourceManager is a static class so the two go well hand-in-hand. This is a nice helper function and it works effectively and my program renders the result just fine. The problem is what I have to do when I'm dealing with other Resources; that means for every Resource that exists, there has to be a type-conversion function to accommodate it. Now THAT is annoying. Isn't there a way I can write a generic type-conversion function something like this?
auto Renderer::getResource(classTypeYouWant T, const std::string &name)
{
auto s = static_cast<T*>(rm::get(name));
return s;
}
Here, the auto keyword causes the function to derive which type it's supposed to be dealing with and return the result accordingly. My first guess is that I might have to use templates; but the problem with templates is that I can't limit which types get inserted into the function, and I really REALLY don't want floating-point id numbers, char ids, let alone custom-defined ids. It's either string (might change to const char* tbh) or ints or else.
How can I create a generic conversion function like the one described above?
Have you looked at using dynamic_cast? If the conversion fails with dynamic_cast the the pointer will be set to nullptr. So you could either write overloads for each type or you could write a template function where you pass the the type you want to convert to as well as the string or id and if the conversion succeeds or fails return true or false.
template<typename T>
bool Renderer::getResource(T*& type, const std::string &name)
{
type = dynamic_cast<decltype(std::remove_reference<decltype(T)>::type)>(rm::get(name));
if (type == nullptr)
return false;
return true;
}
OK, I did not like the idea of a typeless storage, but maybe you find that basic program as a start point. There are a lot of things which must be beautified, but some work must remain :-)
Again: It is a design failure to solve something in that way!
In addition to your example code this solution provides a minimum of safety while checking for the stored type while recall the element. But this solution needs rtti an this is not available on all platforms.
#include <map>
#include <iostream>
#include <typeinfo>
class ResourcePointerStorage
{
private:
std::map< const std::string, std::pair<void*, const std::type_info*>> storage;
public:
bool Get(const std::string& id, std::pair<void*, const std::type_info*>& ptr )
{
auto it= storage.find( id );
if ( it==storage.end() ) return false;
ptr= it->second;
return true;
}
bool Put( const std::string& id, void* ptr, const std::type_info* ti)
{
storage[id]=make_pair(ptr, ti);
}
};
template < typename T>
bool Get(ResourcePointerStorage& rm, const std::string& id, T** ptr)
{
std::pair<void*, const std::type_info*> p;
if ( rm.Get( id,p ))
{
if ( *p.second != typeid(T)) { return false; }
*ptr= static_cast<T*>(p.first);
return true;
}
else
{
return 0;
}
}
template < typename T>
void Put( ResourcePointerStorage& rm, const std::string& id, T *ptr)
{
rm.Put( id, ptr, &typeid(T) );
}
class Car
{
private:
int i;
public:
Car(int _i):i(_i){}
void Print() { std::cout << "A car " << i << std::endl; }
};
class Animal
{
private:
double d;
public:
Animal( double _d):d(_d) {}
void Show() { std::cout << "An animal " << d << std::endl; }
};
int main()
{
ResourcePointerStorage store;
Put( store, "A1", new Animal(1.1) );
Put( store, "A2", new Animal(2.2) );
Put( store, "C1", new Car(3) );
Animal *an;
Car *car;
if ( Get(store, "A1", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "A2", &an)) { an->Show(); } else { std::cout << "Error" << std::endl; }
if ( Get(store, "C1", &car)) { car->Print(); } else { std::cout << "Error" << std::endl; }
// not stored object
if ( Get(store, "XX", &an)) { } else { std::cout << "Expected false condition" << std::endl; }
// false type
if ( Get(store, "A1", &car)) { } else { std::cout << "Expected false condition" << std::endl; }
};
I've found the solution to my question. I created a macro:
#define convert(type, func) dynamic_cast<type>(func)
Extremely generic and code-neutral which allows types to be dynamic_casted from the return type of the function. It also allows for doing checks:
if (!convert(ShaderProgram*, rm::get("skyboxShader")))
cerr << "Conversion unsuccessful!" << endl;
else cout << "Conversion successful!" << endl;
I hope my solution will help people who search for questions similar of this kind. Thanks all!

Parameter validation C++

I've been thinking of a solution to validate the set of parameters a function/method receives using an object oriented approach. For example, in the following snippet the parameters are checked "manually" before being used.
InstallData::InstallData(std::string appPath, std::string appName,
std::string errMsg) {
if(appPath.empty()) {
#ifndef NDEBUG
std::cout << "Path not specified" << std::endl;
#endif
}
if(appName.empty()) {
#ifndef NDEBUG
std::cout << "Application name not specified" << std::endl;
std::cout << "Defaulting to AppName" << std::endl;
this->appName = "AppName";
#endif
}
if(errMsg.empty()) {
#ifndef NDEBUG
std::cout << "Error message not specified" << std::endl;
std::cout << "Defaulting to Error" << std::endl;
this->errMsg = "Error";
#endif
}
// ... further initialization beyond this point ...
}
As the number of parameters increases so does the size of the validation code. I've thought of a basic approach of checking parameters(strings and pointers) as whether they are either empty or null(the aim is to make the code providing functionality more readable).
class Validator {
public:
bool validateStrs(std::vector<std::string> strings, std::vector<std::string> messages, bool quiet);
bool validateStr(std::string str, std::string message, bool quiet);
bool validatePtrs(std::vector<void*> ptrs, std::vector<std::string> messages, bool quiet);
bool validatePtr(void* ptr, std::string message, bool quiet);
};
The validation methods validateStrs and validatePtrs check whether each element of the first array is empty or null and display a message from the second array(there is a one to one relationship between the elements of the first array and the second) if the quiet flag is not set.
In my implementation this looks like:
InstallData::InstallData(std::string appPath, std::string appName,
std::string errMsg, std::string errTitle) {
// Initialize string container
std::vector<std::string> strings;
strings.push_back(appPath);
strings.push_back(appName);
strings.push_back(errMsg);
strings.push_back(errTitle);
// Initialize name container
std::vector<std::string> names;
names.push_back("ApplicationPath");
names.push_back("ApplicationName");
names.push_back("ErrorMessage");
names.push_back("ErrorTitle");
boost::shared_ptr<Validator> valid(new Validator());
bool result = true;
#ifndef NDEBUG
result = valid->validateStrs(strings, names, false);
#else
result = valid->validateStrs(strings, names, true);
#endif
if(result){
this->appPath = appPath;
this->appName = appName;
this->errMsg = errMsg;
this->errTitle = errTitle;
} else {
std::exit(0);
}
}
The messages can also be placed in a separate file thus making the method body cleaner.
Numeric value range checkers can also be implemented similarly. This approach, however, doesn't consider dependencies between parameters.
Is there a more elegant solution of implementing a parameter validation mechanism, possibly using templates?
A more elegant way is not to use standard types for parameters but to define specific classes that check parameters on construction. Something like
class InvalidAppPath {};
class AppPath {
public:
AppPath(const std::string & appPath) : path(appPath) {
if ( appPath.empty() ) throw InvalidAppPath();
}
operator std::string() { return path; }
private:
std::string path;
};
This would also make it easier to ensure that an AppPath is checked for validity only on construction and possibly on modification.
These slides from a presentation by Ric Parkin at the 2007 ACCU Conference explore the idea in greater detail.
Perhaps you would find it easier to leverage function name overloading and variadic templates. You can group the parameter information you want to validate along with the corrective action together in a std::tuple. I implemented a small demo of this idea on IDEONE.
bool validate (std::string s) { return !s.empty(); }
bool validate (const void *p) { return p; }
template <typename Tuple>
bool validate (Tuple param) {
if (validate(std::get<0>(param))) return true;
#ifndef NDEBUG
std::cout << "Invalid: " << std::get<1>(param) << std::endl;
std::get<2>(param)();
#endif
return false;
}
bool validate () { return true; }
template <typename T, typename... Params>
bool validate (T param, Params... params) {
return validate(param) & validate(params...);
}
Then, you could use it like:
bool result
= validate(
std::make_tuple(appPath, "ApplicationPath",
[&](){ appPath = "defaultPath"; }),
std::make_tuple(appName, "ApplicationName",
[&](){ appName = "defaultName"; })
//...
);

Concatenate strings in function call c++

I'm trying to create a logger function where you can pass in a message which will be logged to a text file. Sometimes I'd like to pass in a variable concatenated with my message so I could do something like:
logger("The variable is: " + variable);
The function is defined as
void logger(std::string message);
I'm using Qt, so I don't know if it's relevant but the variable will always be a QString.
When I tried this it would say that no candidate function for
void logger(const QString);
So I thought why not make a second function where it would expect a concatenation:
void logger(std::string message);
void logger2(const QString message);
It compiled fine when I did
logger2("The variable is: " + variable);
However, when I debugged the passed message variable was an empty string.
How do I get this to work, is it possible?
Why not just do something like this:
QString qs = "hello";
qs.toStdString();
As far as concatenating on the fly, I like to use a simple formatter class:
class Formatter
{
public:
template<class Val> Formatter& operator<<(const Val& val)
{
ss_ << val;
return * this;
}
operator string () const { return ss_.str().c_str(); }
private:
std::stringstream ss_;
};
...which can be used like this:
logger(Formatter() << "The variable is: " << variable);
have you tried logger("The variable is: " + variable.toStdString());
?

Custom stream to method in C++?

I'm making a logger and I wish to have some kind of stream-like happenings going on, ideally doing CLogger << "Testing, " << 1 << ",2,3\n"; instead of CLogger->log("Testing, %i,2,3", 1);
My question is how would I do this? I don't want to directly create a stream to stdout as I want to use my own method which includes writing files and such. I've considered overloading with a certain struct that'd flush the current stream buffer to a method, but I'd have to do CLogger << flush << "Test!\n"; which is kind of odd.
Does anybody know how to do this?
If all that you need is directing certain log messages to files, have you considered std::ofstream?
Otherwise, I like to derive my logging class from std::ostream, so I get all of the stream goodness. The trick is to put all of your application-specific code in the associated streambuf class. Consider:
#include <iostream>
#include <sstream>
class CLogger : public std::ostream {
private:
class CLogBuf : public std::stringbuf {
private:
// or whatever you need for your application
std::string m_marker;
public:
CLogBuf(const std::string& marker) : m_marker(marker) { }
~CLogBuf() { pubsync(); }
int sync() {
std::cout << m_marker << ": " << str();
str("");
return std::cout?0:-1;
}
};
public:
// Other constructors could specify filename, etc
// just remember to pass whatever you need to CLogBuf
CLogger(const std::string& marker) : std::ostream(new CLogBuf(marker)) {}
~CLogger() { delete rdbuf(); }
};
int main()
{
CLogger hi("hello");
CLogger bye("goodbye");
hi << "hello, world" << std::endl;
hi << "Oops, forgot to flush.\n";
bye << "goodbye, cruel world\n" << std::flush;
bye << "Cough, cough.\n";
}
Notes:
The CLogger constructor can take whatever parameters you need to use -- a filename, an output language, a pointer to the underlying log data, whatever. Just pass the data onto the CLogBuf class.
The CLogBuf's sync() is automatically called during in response to std::flush.
Check out operator <<, which is what STL's streams overload.
class CLogger
{
public:
CLogger& operator << (const std::string& _rhs)
{
// work with it here
return *this;
}; // eo operator <<
}; // eo class CLogger
EDIT:
See this page that outlines how std::ostream overloads operator << for different types:
http://www.cplusplus.com/reference/iostream/ostream/operator%3C%3C/
Implement a proxy object that gives you operator<< and pass an ownership marker to the returned proxy object. When an object with the ownership marker dies, you flush the stream.
An easy way to do this would be to wrap ostringstream in an auto_ptr in your proxy and flushing to your logger when the auto_ptr is not null in the proxy's d-tor.
That'll give you the formatting possible with ostream, but still result in only one call to your logger, which I thought was the real problem.
Think of something like this:
class CLoggingProxy
{
public:
template <class T>
CLoggingProxy operator<<( const T& rhs )
{
if ( stream )
*stream << rhs;
return *this;
}
~CLoggingProxy()
{
if ( stream )
logger->log(stream->str());
}
private:
std::auto_ptr<std::ostringstream> stream;
CLogger* logger;
friend class CLogger;
CLoggingProxy( CLogger* logger ) // call this e.g. from the logger to "start" input
: stream(new std::ostringstream), logger(logger) {}
};
All of the operator<<() functions are defined on the class ostream, which you can inherit from and implement its methods.
I'm just going to copy-paste my current implementation of this below, it does all you need (and handles things like std::endl and the like). AMBROSIA_DEBUGis macro defined in debug builds, so in theory, every call to this output class should be omitted in release builds (haven't checked though, but seems logical overhead is kept to a minimum. The functionality is based on QDebug functionality, plus a little addition of mine debugLevel, which would allow you to filter debug messages by hand in your code depending on a runtime parameter. Right now it also adds the same amount of spaces before each message.
// C++ includes
#include <iostream>
#include <string>
typedef std::ostream& (*STRFUNC)(std::ostream&);
#ifdef AMBROSIA_DEBUG
static int debugLevel;
const static int maxDebugLevel = 9;
#endif
class Debug
{
public:
#ifdef AMBROSIA_DEBUG
Debug( const int level = 0 )
: m_output( level <= debugLevel ),
m_outputSpaces( true ),
m_spaces( std::string(level, ' ') )
#else
Debug( const int )
#endif // AMBROSIA_DEBUG
{}
template<typename T>
#ifdef AMBROSIA_DEBUG
Debug& operator<<( const T &output )
{
if( m_output )
{
if( m_outputSpaces )
{
m_outputSpaces = false;
std::cerr << m_spaces;
}
std::cerr << output;
}
#else
Debug& operator<<( const T & )
{
#endif // AMBROSIA_DEBUG
return *this;
}
// for std::endl and other manipulators
typedef std::ostream& (*STRFUNC)(std::ostream&);
#ifdef AMBROSIA_DEBUG
Debug& operator<<( STRFUNC func )
{
if( m_output )
func(std::cerr);
#else
Debug& operator<<( STRFUNC )
{
#endif // AMBROSIA_DEBUG
return *this;
}
private:
#ifdef AMBROSIA_DEBUG
bool m_output;
bool m_outputSpaces;
std::string m_spaces;
#endif // AMBROSIA_DEBUG
};
Example usage:
int main()
{
debugLevel = 9; // highest allowed in my app...
Debug(4) << "This message should have an indentation of 4 spaces." << endl;
Debug(8) << "This is a level 8 debug message.\n";
return 0;
}

Is there a __CLASS__ macro in C++?

Is there a __CLASS__ macro in C++ which gives the class name similar to __FUNCTION__ macro which gives the function name
The problem with using typeid(*this).name() is that there is no this pointer in a static method call. The macro __PRETTY_FUNCTION__ reports a class name in static functions as well as method calls. However, this will only work with gcc.
Here's an example of extracting the information through a macro style interface.
inline std::string methodName(const std::string& prettyFunction)
{
size_t colons = prettyFunction.find("::");
size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
size_t end = prettyFunction.rfind("(") - begin;
return prettyFunction.substr(begin,end) + "()";
}
#define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__)
The macro __METHOD_NAME__ will return a string of the form <class>::<method>(), trimming the return type, modifiers and arguments from what __PRETTY_FUNCTION__ gives you.
For something which extracts just the class name, some care must be taken to trap situations where there is no class:
inline std::string className(const std::string& prettyFunction)
{
size_t colons = prettyFunction.find("::");
if (colons == std::string::npos)
return "::";
size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
size_t end = colons - begin;
return prettyFunction.substr(begin,end);
}
#define __CLASS_NAME__ className(__PRETTY_FUNCTION__)
The closest thing there's is to call typeid(your_class).name() - but this produces compiler specific mangled name.
To use it inside class just typeid(*this).name()
Not yet. (I think __class__ is proposed somewhere). You can also try to extract class part from __PRETTY_FUNCTION__.
I would like to suggest boost::typeindex, which I learned about from Scott Meyer's "Effective Modern C++" Here's a basic example:
Example
#include <boost/type_index.hpp>
class foo_bar
{
int whatever;
};
namespace bti = boost::typeindex;
template <typename T>
void from_type(T t)
{
std::cout << "\tT = " << bti::type_id_with_cvr<T>().pretty_name() << "\n";
}
int main()
{
std::cout << "If you want to print a template type, that's easy.\n";
from_type(1.0);
std::cout << "To get it from an object instance, just use decltype:\n";
foo_bar fb;
std::cout << "\tfb's type is : "
<< bti::type_id_with_cvr<decltype(fb)>().pretty_name() << "\n";
}
Compiled with "g++ --std=c++14" this produces the following
Output
If you want to print a template type, that's easy.
T = double
To get it from an object instance, just use decltype:
fb's type is : foo_bar
I think using __PRETTY_FUNCTION__ is good enough though it includes namespace as well i.e. namespace::classname::functionname until __CLASS__ is available.
If you need something that will actually produce the class name at compile time, you can use C++11 to do this:
#define __CLASS__ std::remove_reference<decltype(classMacroImpl(this))>::type
template<class T> T& classMacroImpl(const T* t);
I recognize that this is not the same thing as __FUNCTION__ but I found this post while looking for an answer like this. :D
I created a function using __PRETTY_FUNCTION__ and constexpr with C++17 constexpr std::string_view methods. I also updated the algorithm a bit to be more reliably (Thanks to #n. 'pronouns' m for your help in 64387023).
constexpr std::string_view method_name(const char* s)
{
std::string_view prettyFunction(s);
size_t bracket = prettyFunction.rfind("(");
size_t space = prettyFunction.rfind(" ", bracket) + 1;
return prettyFunction.substr(space, bracket-space);
}
#define __METHOD_NAME__ method_name(__PRETTY_FUNCTION__)
In C++20, one can declare the function as consteval forcing it to evaluate at compile-time. Furthermore, there is std::basic_fixed_string for use as template parameter.
If your compiler happens to be g++ and you are asking for __CLASS__ because you want a way to get the current method name including the class, __PRETTY_FUNCTION__ should help (according to info gcc, section 5.43 Function Names as Strings).
If you're talking MS C++ (You should state, esp as __FUNCTION__ is a non-standard extension), there are __FUNCDNAME__ and __FUNCSIG__ symbols which you could parse
You can get the function name including class name.
This can process C-type funcitons.
static std::string methodName(const std::string& prettyFunction)
{
size_t begin,end;
end = prettyFunction.find("(");
begin = prettyFunction.substr(0,end).rfind(" ") + 1;
end -= begin;
return prettyFunction.substr(begin,end) + "()";
}
My solution:
std::string getClassName(const char* fullFuncName)
{
std::string fullFuncNameStr(fullFuncName);
size_t pos = fullFuncNameStr.find_last_of("::");
if (pos == std::string::npos)
{
return "";
}
return fullFuncNameStr.substr(0, pos-1);
}
#define __CLASS__ getClassName(__FUNCTION__)
I works for Visual C++ 12.
Here's a solution based on the __FUNCTION__ macro and C++ templates:
template <class T>
class ClassName
{
public:
static std::string Get()
{
// Get function name, which is "ClassName<class T>::Get"
// The template parameter 'T' is the class name we're looking for
std::string name = __FUNCTION__;
// Remove "ClassName<class " ("<class " is 7 characters long)
size_t pos = name.find_first_of('<');
if (pos != std::string::npos)
name = name.substr(pos + 7);
// Remove ">::Get"
pos = name.find_last_of('>');
if (pos != std::string::npos)
name = name.substr(0, pos);
return name;
}
};
template <class T>
std::string GetClassName(const T* _this = NULL)
{
return ClassName<T>::Get();
}
Here's an example of how this could be used for a logger class
template <class T>
class Logger
{
public:
void Log(int value)
{
std::cout << GetClassName<T>() << ": " << value << std::endl;
std::cout << GetClassName(this) << ": " << value << std::endl;
}
};
class Example : protected Logger<Example>
{
public:
void Run()
{
Log(0);
}
}
The output of Example::Run will then be
Example: 0
Logger<Example>: 0
This works quite nicely if you are willing to pay the cost of a pointer.
class State
{
public:
State( const char* const stateName ) :mStateName( stateName ) {};
const char* const GetName( void ) { return mStateName; }
private:
const char * const mStateName;
};
class ClientStateConnected
: public State
{
public:
ClientStateConnected( void ) : State( __FUNCTION__ ) {};
};
Works with msvc and gcc too
#ifdef _MSC_VER
#define __class_func__ __FUNCTION__
#endif
#ifdef __GNUG__
#include <cxxabi.h>
#include <execinfo.h>
char *class_func(const char *c, const char *f)
{
int status;
static char buff[100];
char *demangled = abi::__cxa_demangle(c, NULL, NULL, &status);
snprintf(buff, sizeof(buff), "%s::%s", demangled, f);
free(demangled);
return buff;
}
#define __class_func__ class_func(typeid(*this).name(), __func__)
#endif
All the solutions posted above that rely on the __PRETTY_FUNCTION__ do have specific edge case(s) where they do not return the class name / class name only. For example, consider the following pretty function value:
static std::string PrettyFunctionHelper::Test::testMacro(std::string)
Using the last occurence of "::" as delimter won't work since the function parameter also contains a "::" (std::string).
You can find similar edge cases for "(" as delimiter and more.
The only solution I found takes both the __FUNCTION__ and __PRETTY_FUNCTION__ macros as parameters. Here is the full code:
namespace PrettyFunctionHelper{
static constexpr const auto UNKNOWN_CLASS_NAME="UnknownClassName";
/**
* #param prettyFunction as obtained by the macro __PRETTY_FUNCTION__
* #return a string containing the class name at the end, optionally prefixed by the namespace(s).
* Example return values: "MyNamespace1::MyNamespace2::MyClassName","MyNamespace1::MyClassName" "MyClassName"
*/
static std::string namespaceAndClassName(const std::string& function,const std::string& prettyFunction){
//AndroidLogger(ANDROID_LOG_DEBUG,"NoT")<<prettyFunction;
// Here I assume that the 'function name' does not appear multiple times. The opposite is highly unlikely
const size_t len1=prettyFunction.find(function);
if(len1 == std::string::npos)return UNKNOWN_CLASS_NAME;
// The substring of len-2 contains the function return type and the "namespaceAndClass" area
const std::string returnTypeAndNamespaceAndClassName=prettyFunction.substr(0,len1-2);
// find the last empty space in the substring. The values until the first empty space are the function return type
// for example "void ","std::optional<std::string> ", "static std::string "
// See how the 3rd example return type also contains a " ".
// However, it is guaranteed that the area NamespaceAndClassName does not contain an empty space
const size_t begin1 = returnTypeAndNamespaceAndClassName.rfind(" ");
if(begin1 == std::string::npos)return UNKNOWN_CLASS_NAME;
const std::string namespaceAndClassName=returnTypeAndNamespaceAndClassName.substr(begin1+1);
return namespaceAndClassName;
}
/**
* #param namespaceAndClassName value obtained by namespaceAndClassName()
* #return the class name only (without namespace prefix if existing)
*/
static std::string className(const std::string& namespaceAndClassName){
const size_t end=namespaceAndClassName.rfind("::");
if(end!=std::string::npos){
return namespaceAndClassName.substr(end+2);
}
return namespaceAndClassName;
}
class Test{
public:
static std::string testMacro(std::string exampleParam=""){
const auto namespaceAndClassName=PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__);
//AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<namespaceAndClassName;
assert(namespaceAndClassName.compare("PrettyFunctionHelper::Test") == 0);
const auto className=PrettyFunctionHelper::className(namespaceAndClassName);
//AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<className;
assert(className.compare("Test") == 0);
return "";
}
};
}
#ifndef __CLASS_NAME__
#define __CLASS_NAME__ PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__)
#endif
Following method (based on methodName() above) can also handle input like "int main(int argc, char** argv)":
string getMethodName(const string& prettyFunction)
{
size_t end = prettyFunction.find("(") - 1;
size_t begin = prettyFunction.substr(0, end).rfind(" ") + 1;
return prettyFunction.substr(begin, end - begin + 1) + "()";
}