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());
?
Related
I want to create a simple but generic callback with variadic parameters. I also need storing them, because callback will called later in time (from a different thread which uses the storage).
Here's what I have until now:
#include <iostream>
#include <string>
using namespace std;
void fileMgr_DoneWithOtherstuff_DoTheReads() {
FROM SOMESTORAGE get the callback and params
Do the reading
string result = "CONTENT";
Call the callback with result string and params
}
template<class ReadFileCallback, class ...T>
void fileMgr_ReadWithCallback(string filename, ReadFileCallback callback, T ...params) {
cout << "fileMgr_ReadWithCallback is processing file: " << filename << endl;
// string content = "CONTENT";
// callback(content, params...);
SOMESTORAGE = callback + params;
}
void readFileResult(string contents, int id) {
cout << "readFileResult callback: contents=" << contents << ", id=" << id << endl;
}
void readFile(string filename, int id) {
fileMgr_ReadWithCallback(filename, readFileResult, id);
}
int main()
{
int fileId = 1;
readFile("myfile", fileId);
return 0;
}
But I don't want to call callback in fileMgr_ReadWithCallback, because it should be an async method, so I'd rather store the callback function and its parameters, and use it later once File Manager is able to perform the operation.
So the question is, what's the way to store ReadFileCallback and ...T into a struct maybe?
Or if there is better way doing this, let me know please.
I need the variadic arguments, as sometimes the extra param is just an int, but sometimes it can be 2 ints, or 1 string, etc, and I want to keep the FileManager generic.
I'm limited to C++11, but Boost is available.
First of all, relying on a global variable is not a really good idea, because it will give wrong results if you call readFile while a previous read is still in progress. So you really should encapsulate that whole reading process in a class.
You need to use a lambda or std::bind because the parameters of callback are not known outside of the fileMgr_ReadWithCallback function.
So you create a wrapping callback accepting a std::string as a parameter. And use a lambda to capture the parameters, the lambda accepts a string as a parameter, and passes the string and the params to the callback:
storage = [=](std::string s) {
callback(s, params...);
};
And that's how the code then could look like (but as I already said that's bad design)
#include <functional>
#include <iostream>
std::function<void(std::string)> storage;
void fileMgr_DoneWithOtherstuff_DoTheReads() {
std::string result = "CONTENT";
storage(result);
}
template<class ReadFileCallback, class ...T>
void fileMgr_ReadWithCallback(std::string filename, ReadFileCallback callback, T ...params) {
std::cout << "fileMgr_ReadWithCallback is processing file: " << filename << std::endl;
storage = [=](std::string s) {
callback(s, params...);
};
}
void readFileResult(std::string contents, int id) {
std::cout << "readFileResult callback: contents=" << contents << ", id=" << id << std::endl;
}
void readFile(std::string filename, int id) {
fileMgr_ReadWithCallback(filename, readFileResult, id);
}
int main()
{
int fileId = 1;
readFile("myfile", fileId);
fileMgr_DoneWithOtherstuff_DoTheReads();
return 0;
}
You could store your parameters in a tuple via auto storedParams = std::make_tuple(params...). And later you can call your callback function simply with std::apply(callback, storedParams ).
To add an additional parameter (like content) you can use std::tuple_cat(std::make_tuple(content), storedParams ).
I would store the parameters and the callback separately.
BTW: std::apply is only available with C++17 - is there something to emulate that (perhaps in boost)? Otherwise I would try to use the implmentations for std::invoke and std::apply which are shown on cppreference (just remove constexpr...).
Here you can find the code: https://godbolt.org/z/z9MP3M
But you need to add std::apply (that is the most difficult part). Why you need to use such an old compiler?
Update
Perhaps you could use the type erasure features of std::function, see here:
#include <iostream>
#include <string>
#include <functional>
static std::function<void(std::string)> fileManagerStorage;
void fileMgr_DoneWithOtherstuff_DoTheReads() {
//FROM SOMESTORAGE get the callback and params
//Do the reading
std::string content = "CONTENT";
fileManagerStorage(content);
//Call the callback with result string and params
fileManagerStorage(content);
}
template<class ReadFileCallback, class ...T>
void fileMgr_ReadWithCallback(std::string filename, ReadFileCallback callback, T ...params) {
std::cout << "fileMgr_ReadWithCallback is processing file: " << filename << std::endl;
// string content = "CONTENT";
// callback(content, params...);
fileManagerStorage = [=](std::string content){
callback(content, params...);
};
}
void readFileResult(std::string contents, int id) {
std::cout << "readFileResult callback: contents=" << contents << ", id=" << id << std::endl;
}
void readFile(std::string filename, int id) {
fileMgr_ReadWithCallback(filename, readFileResult, id);
}
int main()
{
int fileId = 1;
readFile("myfile", fileId);
return 0;
}
(You should store the storage not as a global variable - store it in your thread...)
In my utils class I have a private solution dir string - it is set in the constructor (hardcoded). I have a getter function that returns the string. In another file I have a utils instance (pointer). When I call the getter function it returns empty.
main.cpp
utils* myUtils = new utils();
std::cout << myUtils->getSolutionDir() + " is the current directory" << std::endl;
delete myUtils;
utils.hpp
public:
utils ();
~utils ();
std::string getSolutionDir();
private:
std::string _solutionDir;
utils.cpp
utils::utils () {
std::string _solutionDir = "C:\\Users\\user\\Documents\\Coding\\CodeBlocks\\MettaRPG";
}
utils::~utils () {}
std::string utils::getSolutionDir() {
return _solutionDir;
}
OUTPUT (GCC compiler):
is the current directory
Take a look at this line in the constructor:
std::string _solutionDir = "C:\\Users\\user\\Documents\\Coding\\CodeBlocks\\MettaRPG";
This line declares a local variable named _solutionDir and sets that equal to the indicated string, rather than taking the existing data member named _solutionDir and changing its value. To address this, remove the std::string from this line.
Alternatively, if you have a C++11 compiler, consider just changing your class to look like this:
public:
~utils ();
std::string getSolutionDir();
private:
std::string _solutionDir = "C:\\Users\\user\\Documents\\Coding\\CodeBlocks\\MettaRPG";
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"
I have a 3rd party (logging) class that overloads the << operator. The client code using this logger class can use this by calling one of the pre-defined macros. As an example:
//logs can be filtered based on this module id string
LOGGER_INFO("MODULE_ID_STR") << "Logging at info level";
I'd like to extend this feature wherein the class/module using this 3rd party logger does not have to include the module id string each time. Meaning - the client code should set the module id string once and then be able to do this:
cLogger.INFO << "Logging at info level";
The above call should internally use the registered module id string registered earlier and then use that to make the actual 3rd party log call. So can this be done elegantly in C++ by overloading the << operator for each of the log levels.
Some additional details...I started out by doing this:
This is the class that extends the functionality of the 3rd party logger:
class LoggerEx
{
public:
LoggerEx(const std::string &moduleToLog)
{
m_ModuleID = moduleToLog;
};
virtual ~LoggerEx() {};
class Debug
{
//overload the << operator (how to write this..??)
LOGGER_INFO(m_ModuleID) << "Logging at info level";
};
class Info
{
//overload the << operator
};
//Note that there could be more such levels
// (INFO, WARN, ERROR, TRACE, FATAL, etc).
public:
Debug DEBUG;
Info INFO;
protected:
std::string m_ModuleID
};
Some client code using the logger class should be allowed to do this...
class Xyz
{
public:
Xyz() : l("Xyz")
{}
void doSomething()
{
l.DEBUG << "Doing something";
}
protected:
Logger l;
};
Another client class...
class Mno
{
public:
Xyz() : l("Mno")
{}
void processSomething()
{
l.INFO << "Process something";
}
protected:
Logger l;
};
Since the original logger supports several data types (int, float, chars, std::string), will the above be the approach, or are there any other ideas/solutions to do this more elegantly in C++ without writing a full blown wrapper (or duplicating code) to the logger?
Thanks...
This is actually harder than one might think, mostly because in a typical logging library, the LOGGER_INFO macro or its equivalents do more than just giving you a stream. Here's a typical macro from Boost:
#define BOOST_LOG_STREAM_WITH_PARAMS_INTERNAL(logger, rec_var, params_seq)\
for (::boost::log::record rec_var = (logger).open_record((BOOST_PP_SEQ_ENUM(params_seq))); !!rec_var;)\
::boost::log::aux::make_record_pump((logger), rec_var).stream()
A quick look at this code shows that it creates a new record, creates a pump, gets the stream from this pump, and your << "log text here" << " more log stuff" calls actually operates on that stream. It is when the pump and record gets destructed, at the end of the statement, that the message actually get pushed out into a single log entry, which makes sense when you think of it - you'd expect LOGGER_INFO(m_ModuleID) << "Logging at info level" << "more text"; to produce one log entry instead of two.
Thus a naive implementation like
class LoggerEx
{
public:
LoggerEx(const std::string &moduleToLog) : Debug(moduleToLog)
{ }
~LoggerEx() {}
class Debug
{
private:
std::string m_ModuleID;
public:
Debug(const std::string &module) : m_ModuleID(module) {}
template <typename T>
const Debug & operator << (const T& thing_to_log) const {
LOGGER_INFO(m_ModuleID) << thing_to_log;
return *this;
}
};
public:
Debug DEBUG;
};
will only work if you only use << once per statement in your logging code.
One possible way of getting around it would be to use an internal stream to store the log-entry-in-making:
class LoggerEx
{
public:
LoggerEx(const std::string &moduleToLog) : m_module(moduleToLog)
{ }
~LoggerEx() {}
class Debug
{
private:
std::string m_ModuleID;
std::stringstream m_ss;
public:
Debug(const std::string &module) : m_ModuleID(module) {}
Debug(const Debug &other) : m_ModuleID(other.m_ModuleID) {}
~Debug() {
std::string str = m_ss.str();
if(!str.empty())
LOGGER_INFO(m_ModuleID) << str;
}
template <typename T>
Debug & operator << (const T& thing_to_log) {
m_ss << thing_to_log;
return *this;
}
};
public:
Debug DEBUG() { return Debug(m_module);}
private:
std::string m_module;
};
It would be called like
l.DEBUG() << "Some stuff " << some_number << " some more stuff";
The idea is that the DEBUG() call produces a temporary object; your operator << calls on that temporary object writes stuff into the stringstream, and at the end of the line, when the temporary object gets destructed, the things in the stringstream get pushed out to the logging library.
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) + "()";
}