I implemented a very simple (error) logger class. It looks like this:
#pragma once
#include <string>
extern const char* LOG_DEFAULT_TEXT = "<LOG>\n\n";
class Log
{
public:
Log() : _logs(0), _log(LOG_DEFAULT_TEXT) {};
void add(const char *str, bool nl=true);
void clear();
const char* get() const { return _log.c_str(); }
int getNumLogs() const { return _logs; }
private:
std::string _log;
int _logs;
};
Now my question is, say I have this Main class, that contains all the other objects that my program may contain, and also this Log class.
Obviously I would want these "other objects" in my Main class to be able to use the Log object in Main, so the easy solution would be to pass pointers to every class' constructor so that it could use the Log object.
I'm talking about something like this:
//Main.h
...
#include "Log.h"
class Main() {
public:
...
private:
Log _log;
ImportantObject1(&_log); //pass a pointer to _log
ImportantObject2(&_log); //..same
};
This solution seems too clumsy, so I'm asking if there are different approaches for what I want to accomplish, which is error logging.
That's one of the rare cases a singleton makes sense:
class Log
{
public:
void add(const char *str, bool nl=true);
void clear();
const char* get() const { return _log.c_str(); }
int getNumLogs() const { return _logs; }
static Log& instance() {
static Log theInstance;
return theInstance;
}
private:
Log() : _logs(0), _log(LOG_DEFAULT_TEXT) {};
std::string _log;
int _logs;
};
This way you can use it elsewhere just accessing
Log::instance().add("Blah Blah",true);
Another approach to address the requirement...
Use functions in a suitable namespace instead of a singleton.
Log.h:
namespace Log
{
void add(const char *str, bool nl=true);
void clear();
const char* get();
int getNumLogs();
}
Log.cpp:
namespace detail
{
// This is almost same as the class from OP's post.
struct LogImpl
{
LogImpl(std::string const& log) : _logs(0), _log(log) {};
void add(const char *str, bool nl=true);
void clear();
const char* get() const { return _log.c_str(); }
int getNumLogs() const { return _logs; }
std::string _log;
int _logs;
};
}
namespace Log
{
// This mimics the singleton implementation...
// Create only one instance of LogImpl.
static detail::LogImpl impl("<LOG>\n\n");
void add(const char *str, bool nl)
{
impl.add(str, nl);
}
void clear()
{
impl.clear();
}
const char* get()
{
return impl.get();
}
int getNumLogs()
{
return impl.getNumLogs();
}
}
namespace Log
{
void add(const char *str, bool nl=true);
void clear();
const char* get();
int getNumLogs();
}
Using functions in a namespace vs using a singleton
To support use of functions in a namespace requires bit more code.
However, it simplifies calling code.
Calling code can use
Log::add(...);
instead of
Log::instance()->add(...);
Another common pattern inherited from C would be a global object. Globals are generally frowned upon because they are the symptom of bad design. But it makes sense for a logger to be a global.
That means that the header for your class should contain :
extern Log log;
and the cpp
#include <log.h>
...
Log log; // unique definition for the global Log
Then in any module using the log :
#include <log.h>
...
log.add(...);
The caveat here that there will be static initialization of Log log. And C++ is not always very nice with order of static initialization : ... Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of
a variable defined in a different translation unit.. The only foolproof way is to ensure that :
Log construction does not depend on any other statically initialized variable needing a constructor phase in another translation unit - it includes std::string but hopefully not const char *
the global variable is used once before any multithreading starts
If in doubt, use the singleton pattern suggested by πάντα ῥεῖ
Related
I'm planing to use a C lib which have a function requires to register a callback function:
int add_listen(void (*listener)(const char* param));//C code
But I met a "invalid use of const_cast" error when I'm trying to do this:
class A_Interface{
public:
A_Interface():listenerCB{[this](const char* param) {
this->onReceived(std::string(param));
}} {
add_listen(listenerCB.target<void(const char*)>());
};
//will be overrided by sub class
virtual void onReceived(const std::string param) = 0;
private:
std::function<void(const char*)> listenerCB;
}
Is there any other way to pass this onReceived to add_listen?
Wrap the native CB mechanism in a Meyer singleton:
typedef void(cb_t)(char const*);
template<
int(*reg)(cb_t*),
int(*ureg)(cb_t*)=nullptr
>
class wrapped_cb{
public:
~wrapped_cb(){
if (!ureg)
return;
(*ureg)(&call_servant);
};
static auto signal(){
static wrapped_cb cb_list;
return cb_list.signal_prv;
};
private:
wrapped_cb(wrapped_cb const&)=delete;
auto& operator=(wrapped_cb const&)=delete;
wrapped_cb(){
(*reg)(&call_servant);
};
static void call_servant(char const* s){
signal()(s);
};
boost::signal2
<decltype((call_servant))>
signal_prv;
};
//...
wrapped_cb<&add_listen>::signal()
.connect([](char const* s){
std::cout<<s<<std::endl;
};
wrapped_cb<&add_listen>::signal()
.connect([](char const* s){
std::cout<<s<<" v2"<<std::endl;
};
As #SamVarshavchik said:
The library must provide a void * opaque pointer that can be passed through, and synthesized into this, or an equivalent
So seems there is no "pretty" way to pass this onReceived to add_listen, or create a thunk as #Remy Lebeau mentioned.
I have an algorithm (not preseted here) which takes as input different parameters (int, float, vectors).
My idea of design was to have an container which holds all these differents parameters.
To achive this, I have a base class Parameter and a derivated template class TypeParameter.
These parameters will be holded in a container.
The design is presented below:
#pragma once
#include <utility>
#include <memory>
#include <string>
#include <vector>
namespace parameter
{
/*
Interface for parameter
*/
class Parameter
{
public:
Parameter() {}
Parameter(std::string param_name) : name(param_name) {}
Parameter(const Parameter&& other) noexcept : name(std::move(other.name)) {}
virtual ~Parameter() {}
inline const std::string get_name() { return name; }
private:
std::string name;
};
/*
*/
template<class T>
class TypeParameter
: public Parameter
{
public:
TypeParameter(std::string param_name, T new_value) : Parameter(param_name), value(new_value) {}
TypeParameter(const TypeParameter&& other) noexcept : Parameter(std::move(other)), value(std::move(other.T)) {}
inline const T get_value() { return value; }
private:
T value;
};
/*
Container for parameters
*/
class ParameterSet
{
public:
ParameterSet() {}
void add(std::unique_ptr<Parameter> param) { data.push_back(std::move(param)); }
private:
std::vector <std::unique_ptr<Parameter>> data;
};
} //namespace parameter
The main is:
#include <iostream>
#include <string>
#include "Parameter.h"
using parameter::TypeParameter;
using parameter::Parameter;
using parameter::ParameterSet;
void foo(std::unique_ptr<Parameter> p)
{
std::cout << p->get_value(); // ERROR
}
int main(int argc, char *argv[])
{
TypeParameter<int> *iparam = new TypeParameter<int>("ee", 3);
std::unique_ptr<Parameter> p = std::make_unique <TypeParameter<int>>("foo", 3);
foo(std::move(p));
ParameterSet param_set;
param_set.add(std::unique_ptr<Parameter>(iparam));
param_set.add(std::move(p));
getchar();
}
My problem is I cannot get the value without a cast.
Hence, my question is how do I cast the unique_ptr from a Parameter class to derived TypeParameter.
Is there another way to design the container?
Thanks a lot!
You don't have to reinvent the wheel. There are a couple of classes you can use from the standard library:
std::variant.
As suggested by the comments, variant is a type-safe union of a pre-defined set of data types, which you put in the templates argument of variant.
For example, a std::variant<int,float,double> can hold any value of type int, float, or double, but nothing else.
To use the stored value, you can either use the visitor pattern with the std::visit() function. Other functions allow you to know which of the preset types is stored in the variable (index()) and to extract the value from it (using get()). If you try to extract the value of the wrong type, the get() function throws an exception
std::any
is another utility that can hold different data types. As opposed to variant, you don't have to know the types at compile-time. Basically, it stores a void* to the data with a typeinfo to remember its original type. You can then use any_cast to cast the variable back to its original type. Just like variant, an exception is thrown when trying to cast to the wrong type.
These two classes are available in C++ 17. If these features are not available to you, they were also included in boost (respectively boost:variant and boost:any)
You can store the set of values in a standard library container, e.g. in a std::vector<std::variant<int,float,double>> or a std::vector<std::any>>.
Alternative to std::variant/std::any is the old way polymorphism:
class Parameter
{
public:
Parameter(const std::string& param_name) : name(param_name) {}
virtual ~Parameter() = default;
const std::string& get_name() const { return name; }
virtual void printValue() const = 0;
// Other virtual methods
private:
std::string name;
};
template<class T>
class TypeParameter : public Parameter
{
public:
TypeParameter(const std::string& name, const T& t) : Parameter(name), value(t) {}
// Non virtual method when we don't access it by base class.
const T& get_value() const { return value; }
void printValue() const { std::cout << value; }
private:
T value;
};
And then your
void foo(const Parameter& p)
{
std::cout << p.get_value(); // ERROR
}
becomes
void foo(const Parameter& p)
{
p.print();
}
If you don't want to add many virtual methods to Parameter, then Visitor pattern can help, but then you have to know each derived types.
I need to keep a std::map of pointers to templated objects.
To get rid of templates, I use a common untemplated base class.
When running the code, I get a SIGSEGV signal.
Debugging shows that the problem arises with statement
data_[id] = s;
It could be a problem related to the order of initialization of the objects.
The code looks like this:
File shared_iface.h:
class shared_iface {
unsigned long int counter_;
};
File shared.h:
extern CommunicationHandler comm;
template <typename T>
class shared: private shared_iface {
public:
shared(): data_(nullptr), id_(0) {
comm.add(id_, this);
}
private:
T* data_;
unsigned long int id_;
};
File communication_handler.h:
class CommunicationHandler {
public:
inline void add(unsigned long int id, shared_iface* s) {
data_.add(id, s);
}
private:
Dictionary data_;
};
File communication_handler.cpp:
CommunicationHandler comm;
File dictionary.h:
class Dictionary {
public:
Dictionary() {
data_.clear();
}
void add(unsigned long int id, shared_iface* s) {
data_[id] = s;
}
private:
std::map<unsigned long int, shared_iface*> data_;
};
File main.cpp:
#include "shared.hpp"
shared<int> c;
int main ()
{
return 1;
}
It could be a problem related to the order of initialization of the objects.
A good guess. c is a static object of type shared<int>. The constructor ofshared<T> depends on the static object comm. c may very well be initialized before comm and you'll get undefined behaviour. comm could have been initialized first, you're lucky that your code didn't work.
This is known as static initialization order fiasco. The usual way to avoid the fiasco is Construct On First Use Idiom but in general, avoid static objects that depend on other static objects.
I am actually working on a project, which has one file like this.
#include "Preference.h"
#include <string>
#include <list>
#include <exception>
namespace MedicalStudentMatcher
{
class PreferenceException: public std::exception
{
public:
PreferenceException(std::string message) : m_message(message) {}
virtual ~PreferenceException() { }
virtual const char* what() const throw()
{
return m_message.c_str();
}
private:
std::string m_message;
};
class PreferenceReader
{
public:
PreferenceReader(std::string filename);
virtual ~PreferenceReader();
std::list<Preference<std::string, std::string>> ReadPreferences();
private:
std::string m_filename;
std::string trim(std::string str);
};
}
Now the questions are
1. How is the constructor working ? (Please bear in mind that I am a newbie to STL in C++ , and any kind of advanced methods in C++)
2. Explain the syntax of what() function.(Why there are two const then a char * and then a throw)
3. What does the below line mean
std::list<Preference<std::string, std::string>> ReadPreferences();
4. I want to traverse through this list. How do I go about it.?
list<Preference<string, string>> hospitalPrefs = hospitalReader.ReadPreferences();
list<Preference<string, string>> studentPrefs = studentReader.ReadPreferences();
list<Match<string, string>> matches;
5. How do template class work in the following case and how is preference class using it. What is P m_preferrer declare ? How are "initialisation list" working in this case?
template <class P, class O>
class Preference
{
private:
P m_preferrer;
O m_preferred;
int m_value;
public:
Preference(const P& preferrer, const O& preferred, int value) : m_preferrer(preferrer), m_preferred(preferred), m_value(value) {}
virtual ~Preference() {}
P getPreferrer() const { return m_preferrer; }
O getPreferred() const { return m_preferred; }
int getValue() const { return m_value; }
};
template <class P, class O>
bool less_than(const Preference<P, O>& p1, const Preference<P, O>& p2)
{
return p1.getValue() < p2.getValue();
}
}
Even after thorough googling i couldn't find answer to these question.
Please help. if you need any more info on other files, kindly comment.
The PreferenceException constructor uses an "initialization list" to set m_message. Now that you know that term, you can search for it to learn more.
virtual const char* what() const throw() declares "A virtual (runtime polymorphic) function which returns a pointer to (an array of) characters, where that pointer cannot be used to modify those characters." The trailing "const throw()" mean "This function cannot modify its implicit this argument, i.e. it cannot modify the instance of the class on which it was called, and it cannot throw any exceptions."
That's a member function declaration. The function should be defined elsewhere. The function returns a (doubly-linked) list of Preferences.
Try this:
list<Preference<string, string>> hospitalPrefs = hospitalReader.ReadPreferences();
for (Preference<string, string>& pref : hospitalPrefs)
{
// do something with pref
}
Or if you're stuck on C++98 instead of C++11:
list<Preference<string, string>> hospitalPrefs = hospitalReader.ReadPreferences();
for (list<Preference<string, string>>::iterator it = hospitalPrefs.begin(); it != hospitalPrefs.end(); ++it)
{
// do something with pref
}
For some tracing automation for identifying instances i want to call either:
a non-static method of the containing object returning its identifier
something else which always returns the same id
My current solution is to have a base class with a method which() and a global function which() which should be used if not in the context of an object.
This however does not work for static member functions, here the compiler prefers the non-static method over the global one.
Simplified example:
class IdentBase
{
public:
Ident(const std::string& id) _id(id) {}
const std::string& which() const { return _id; }
private:
const std::string _id;
};
const std::string& which() { static const std::string s("bar"); return s; }
#define ident() std::cout << which() << std::endl
class Identifiable : public IdentBase
{
public:
Identifiable() : Ident("foo") {}
void works() { ident(); }
static void doesnt_work() { ident(); } // problem here
};
Can i somehow avoid using work-arounds like a special macro for static member functions (maybe using some template magic)?
Define a function template that returns a default identifier for all types.
template<typename T>
const std::string& which(const T& object)
{ static const std::string s("bar"); return s; }
Specialize the function template for the specific class.
class IdentBase
{
public:
IdentBase(const std::string& id): _id(id) {}
const std::string& id() const { return _id; }
private:
const std::string _id;
};
template<>
const std::string& which(const IdentBase& object)
{ return object.id(); }
Call the function template by passing an instance that you want to identify.
int main()
{
int i;
std::cout << which(i) << std::endl;
IdentBase foo("foo");
std::cout << which(foo) << std::endl;
return 0;
}
You might be able to to use is_member_function_pointer from the Boost TypeTraits library. sbi's suggestion of using different code in the static and non-static cases is probably better though.
Do you need a different identifier for every instance of each class as in your example, or are you just trying to identify which class is in the trace?
Changing your which() function and _id member to static would expose them both to your static member functions, and as a bonus decrease your memory usage.