For logging purposes, I would like to adapt various classes (for this reason I'd like a generic approach) to a key value dictionary : this could be seen as "key value serialization".
Let's assume the keys are pre-defined and that, depending on the input class we do want to adapt, each value may correspond to a specific attribute.
Values can always be encapsulated into an std::string.
This would be my approach :
Create an adapter class which can be dumped into the database
#include <keys.h> // enum with possible keys, defining type Key_t
namespace generic
{
class Adapter
{
public:
Adapter();
virtual ~Adapter();
virtual void init() = 0;
private:
std::map<Key_t, std::string> _data;
}
}
For every possible client, specialize the adapter class in its namespace, supposing it is friend with any client's specific business object model (to access attributes easily), and that it receives the instances of such models via const references in its constructor
e.g.
#include <generic/Adapter.h>
#include <client1/bom1.h>
#include <client1/bom2.h>
...
#include <client1/bomN.h>
namespace client1
{
class Adapter : public generic::Adapter
{
public:
Adapter(const Bom1& bom1,
const Bom2& bom2,
const BomN& bomN)
: _bom1(bom1), _bom2(bom2), _bomN(bomN)
{}
void init()
{
// Explicit data mapping in here
_map[NAME] = _bom1._name;
_map[TITLE] = _bom2._title;
....
....
}
private:
Bom1 _bom1;
Bom2 _bom2;
BomN _bomN;
}
}
What do you think about this approach ?
Is there a more generic way of achieving this in c++ ?
What would have been your design ?
Thanks!
Update
When a new client is implemented the logging engine shouldn't change: that is why the adapting logic should be distributed on client side rather than being implemented in the core of the logging engine.
The logging engine would be updated only if new keys are required (this would probably imply a database structural change).
I would have stored serialized strings for both keys and values.
Here I'm using the ldbSerialize method which uses boost serialization by default and can be easily specialized without creating a new class. For every new type of the key one would simply add a new specalization:
template <> inline void ldbSerialize<Key32> (string& bytes, const Key32& key) {
bytes += key.whatever();
}
Related
The Problem
I want to create a debugging application over my libraries for testing, debugging, ... purposes. But I don't want to give the end-user additional non-necessary APIs.
For example, consider an application that visualizes the program's plugin usage of the library to the end-user. So I can't use standard debuggers like GDB or LLDB with a Release build containing some debugging information. How could be the library/debugging application designed for that?
The Restrictions
Must work on Windows(MSVC-2015) and Linux(GCC-8).
C++11 only.
No debug information.
No core dumps.
No additional public APIs.
The Requirements(If It's Possible)
Accessing the library's main class private members without additional public/protected APIs I think could be enough if there is no other way to do it.
Possible Solution
I just introduce an additional symbol into my main library class as the class friend:
Complete code: MyLibrary.hh
class MyClassPrivate;
class MyClass {
public:
friend class MyClassDebugger;
int value() const;
private:
friend class MyClassPrivate;
std::unique_ptr<MyClassPrivate> impl_;
};
And then I export the class header and the class private header to the user and the user could just define the MyClassDebugger symbol and use it to access to the MyClass private implementations:
Complete code: main.cc
class MyClassDebugger {
public:
void modify(MyClass& object)
{
object.impl_->value = 100;
}
};
int main()
{
MyClass object;
MyClassDebugger().modify(object);
std::cout << object.value() << std::endl;
}
The complete code of the example: https://gist.github.com/gccore/397fb6147280bd32b6fe340aa6ce579a
I would consider dropping your “No additional public APIs” requirement, and implement some reflection interface for your objects, which allows users to list properties exposed to debug interface, get property values, and update these values. Here’s an example how that API may look like.
// Change this enum to contain types of various properties you have in your classes
// For instance, if you don't have nested objects, remove the corresponding entry from the enum
enum struct ePropertyType: uint8_t
{
Empty = 0,
Int32,
FP32,
String,
NestedObject
};
struct iDebugView;
// Variant structure for the values
struct sPropVariant
{
// The type of the value
ePropertyType type = ePropertyType::Empty;
// The value itself
union
{
int int32;
float fp32;
const char* string;
iDebugView* nestedObject;
};
};
// Describes a single field of some particular type
struct sPropertyDesc
{
std::string name;
ePropertyType type;
};
// Debugger interface to view and change private fields of objects
struct iDebugView
{
// Get descriptor for all properties exposed by this object.
// Note this doesn't include the values i.e. only depends on the object's type but not the instance
// Your implementation should return reference of a global variable, possibly lazily initialized on first use
virtual const std::vector<sPropertyDesc>& listProperties() const = 0;
// Get value of the property identified by 0-based index
// Returns ePropertyType::Empty if the index was out of range
virtual sPropVariant getValue( uint32_t index ) const = 0;
// Set value of the property identified by 0-based index.
// Returns false for errors such as index out of range, type mismatch, or trying to set NestedObject property
virtual bool setValue( uint32_t index, const sPropVariant& value ) = 0;
};
While not terribly complicated, that new API should enable GUI similar to PropertyGrid in C#, allowing to inspect and modify private properties of your classes.
In the hello world example of policy based design from wikipedia we use a common interface HelloWorld and configure it with different policies through templates - so far so good:
int main() {
// Example 1
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>
HelloWorldEnglish;
HelloWorldEnglish hello_world;
hello_world.Run(); // Prints "Hello, World!".
// Example 2
// Does the same, but uses another language policy.
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman>
HelloWorldGerman;
HelloWorldGerman hello_world2;
hello_world2.Run(); // Prints "Hallo Welt!".
}
This is all very nice and elegant, but what is the idiomatic way of managing / storing a collection of such configurable objects? For example, one would like to write
std::vector< some_magic_type > seasons_greetings; // What is the common type specifying the public interface only?
seasons_greetings.push_back(hello_world); // which is of type HelloWorldEnglish
seasons_greetings.push_back(hello_world2); // which is of type HelloWorldGerman
for (greeting : seasons_greetings) {
greeting.Run() // access only the public interface
}
In designing interfaces as base classes and deriving specialised implementations from them, I don't have this problem - I can always store pointers to the base class type - but I need to spell out all implementations leading to a whole lot of derived classes.
Policy Based Design promised to alleviate the explosion of derived classes that comes with this by using templates to mix and match behavior. But I pay for this with a whole lot of different types.
There must be an idiomatic way to deal with this. Any insight is greatly appreciated.
P.S.
I admit that I did not buy the book, but you might have guessed already.
This answer suggests storing a collection implies an inheritance based design, but does it really?
If you want to stay with unrelated classes you can use std:variant in combination with std::visit.
Example ( simply extend the main function from the original example )
using Variant = std::variant< HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>, HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman> >;
std::vector< Variant > seasons_greetings;
seasons_greetings.push_back(hello_world);
seasons_greetings.push_back(hello_world2);
for (auto& greeting : seasons_greetings) {
std::visit( [](auto& what){ what.Run(); }, greeting );
}
The bad side: You have to know all possible combinations of policies which can be really uncomfortable. But you can use some meta template stuff to create all variant types by giving type lists for each used policy and the template will generate all combinations, which is not such a big hack.
So here is a solution I found after pondering the comments again - key seems to combine the best of both worlds, polymorphism and policies - who would have thought ....
By introducing a common base class that defines only the public interface of the behavior class and then deriving the behavior class from that and the policies, I can get exactly what I want:
class HelloWorldInterface {
public:
// Behavior method.
void Run() const {
run();
}
private:
virtual void run() const = 0;
};
template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : public HelloWorldInterface, private OutputPolicy, private LanguagePolicy {
private:
using OutputPolicy::Print;
using LanguagePolicy::Message;
void run() const override final {
Print(Message());
}
};
Then, I can write basically what I wanted from the beginning:
int main() {
// Example 1
using HelloWorldEnglish = HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>;
// Example 2
// Does the same, but uses another language policy.
using HelloWorldGerman = HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman>;
HelloWorldEnglish hello_world;
HelloWorldGerman hello_world2;
std::vector<const HelloWorldInterface*> greetings{&hello_world, &hello_world2};
for (auto x : greetings) {
x -> Run();
}
}
This way, one gets a single public interface that exposes behaviours resulting from an arbitrary combination of policies. Looks trivial now, though.
I'd like to make a class Car extendable by allowing injection of a user-made subclass of Engine.
So one user might want a diesel car:
DieselEngine *de = new DieselEngine;
de->setGlowplugTemperature(1200); // something specific for a Diesel
car->setEngine(de);
car->drive();
While the other wants something else:
FluxCapacitorEngine *fce = new FluxCapacitorEngine;
fce->setDestinationYear(1985);
car->setEngine(fce);
car->drive();
Internally, Car calls (pure) virtual methods of its instance of Engine in order to do its business. The issue now is, if at a later time the user wishes to do some more configuring on the engine, he would either have to keep a pointer e.g. of type DieselEngine* externally in order to access it, or use a dynamic cast:
if (DieselEngine *de = dynamic_cast<DieselEngine*>(car->engine()))
de->setMixRatio(2.1);
I don't find either variants particularily nice. Are there alternatives to achieve this kind of customizability/extendability?
A solution that I find lacking (current state): one could leave the implementation of the engine part inside Car, and make the user subclass the whole thing like class Delorean: public Car, so he could directly call the specific methods:
delorean->setDestinationYear(1985); // introduced method with the Delorean class
delorean->drive(); // inherited method of Car
However (and this is where the analogy becomes shaky, bear with me) I want to preserve the option of hot-swapping the engine while the Autobahn and the InsuranceCompany hold pointers to the car. This wouldn't be possible if we subclassed Car because we can't transform a car to a delorean instance without changing its pointer.
Another complication:
The current implementation of my Car doesn't have this extensible, externally settable engine. Instead it's like in the previous paragraph: all the engine parts are in the Car implementation in the form how 80% of my users need their engine. The configuration setters of the engine part of the car are directly and easily accessible via the car's public interface.
So if I now switch to the external engine concept, I'll be upsetting 80% of my users who are happy with the default engine, because instead of
car->setSparkVoltage(1000);
they would then need to write
if (DefaultGasolineEngine *dge = dynamic_cast<DefaultGasolineEngine*>(car->engine()))
dge->setSparkVoltage(1000);
ugh. They don't care that it's a DefaultGasolineEngine, they just want to set their familiar spark voltage.
In Summary: Are there alternatives to achieve this kind of customizability/extendability while maintaining a nice interface for the user to his custom class as well as to the default implementation which will be used by the majority of users?
I feel your pain on this. In my experience, your best option is to add support for a more dynamic way of setting properties: by strings. You can continue to support your existing Engine class interface, but add to it some generic property setters (and getters if you like) that will be implemented by each engine. The same property setters can be exposed by the Car class for convenience, which just call the same on its engine object.
////// base class declarations
class Engine
{
public:
// your existing API here
// support as many value types as you need
virtual bool setProperty(const std::string & name, const std::string & value) = 0;
virtual bool setProperty(const std::string & name, int value) = 0;
virtual bool setProperty(const std::string & name, float value) = 0;
};
class Car
{
public:
// your existing API here
// optional for convenience... replicate from Engine API
bool setEngineProperty(const std::string & name, const std::string & value);
bool setEngineProperty(const std::string & name, int value);
bool setEngineProperty(const std::string & name, float value);
};
////// DieselEngine implementation
// these should be declared in the DieselEngine as public static const, and defined here
const std::string DieselEngine::PROP_SPARK_VOLTAGE = "SparkVoltage";
const std::string DieselEngine::PROP_MIX_RATIO = "MixRatio";
bool DieselEngine::setProperty(const std::string & name, const std::string & value)
{
// definitely do some input validation first!
if (name == PROP_SPARK_VOLTAGE)
{
this->setSparkVoltage(atol(value.c_str()));
return true;
}
else if (name == PROP_MIX_RATIO)
{
this->setMixRatio(atof(value.c_str()));
return true;
}
return false;
}
////// Car implementation
bool Car::setEngineProperty(const std::string & name, const std::string & value)
{
return this->engine->setProperty(name, value);
}
////// Example usage
Car car;
car.setEngine(new DieselEngine());
car.setEngineProperty(DieselEngine::PROP_SPARK_VOLTAGE, "1000");
// OR
car.getEngine().setProperty(DieselEngine::PROP_SPARK_VOLTAGE, "1000");
Another added benefit of this approach is that you can easily begin configuring your engines from a configuration file, since you now support loading properties from human readable strings. A simple name / value pair or JSON file can be loaded in and the property setters can be called with the values.
The reason I am returning bool from the setters is so that you can know if the property was recognized. You should obviously do some input validation on the value itself as well. You could return an int error code instead to indicate various types of errors, or use exceptions if you prefer.
It would also be acceptable to put the property name fields (e.g. DieselEngine::PROP_SPARK_VOLTAGE) in a single namespace (e.g. EngineProps::SPARK_VOLTAGE) and then clearly document which engine types support which engine properties. In that way, your example code would look like:
////// Example usage
Car car;
car.setEngine(new DieselEngine());
car.setEngineProperty(EngineProps::SPARK_VOLTAGE, "1000");
// OR
car.getEngine().setProperty(EngineProps::SPARK_VOLTAGE, "1000");
I'm currently working on a college project with C++ and one of my assignments is to make a social network using inheritance and polymorphism. Currently I have a Node class that is used on a Map and Multimap (both are created manually and not used from the std). The node can hold two variables (key and data for example) and where I'm using it, the first variable can either be a pointer or a string (they let us use std::string).
The problem I'm having is that when I inherit from the "root" class (Object) and use "Object" as a data type for "key", I'm unable to pass a string created with the std as parameter to its constructor, because it doesn't inherit from my Object class. One solution is to implement my own string class and make it inherit from Object, but I was searching for other workarounds.
If there's any problem with the logic above, please tell me as I'm just beginning with C++.
EDIT 1 (some code for my Node):
class TempNode
{
private:
TempNode* next;
Key key;
T value;
public:
TempNode();
explicit TempNode(const Key thisKey, const T thisValue, TempNode* thisNext = NULL)
: key(thisKey)
, value(thisValue)
, next(thisNext)
{
}
inline Key getKey() { return key; }
inline T getValue() { return value; }
inline TempNode* getNext() { return next; }
inline void setNext(TempNode* thisNext) { next = thisNext; }
};
The string or Person types are currently used only in key, but that is with another implementation using templates (which works fine), but my teacher now requires us to apply inheritance to the entire project (to get used to it I guess).
To implement this using inheritance, you think of Key as a data type specifically designed as a key in your map/multimap implementation. Key inherits from Object, but it may provide its own, key-specific functions, such as – for example – a function repr() which generates a representation used by the map for some map-specific operations (maybe as a basis for hashing, or sorting or whatever).
The map/multimap must be used in such a way that the Key objects are stored as pointers (or std::unique_ptr, or std::shared_ptr, or whatever is appropriate), but not as copies of Key objects.
So we have:
struct Object
{
virtual ~Object()
{ }
};
/* Key class. Pointers of this type are inserted
into the map. */
class Key : public Object
{
public:
/* Must be supported by all keys: */
virtual std::string repr() const = 0;
};
We also assume there is a separate definition of Person objects:
struct Person : Object
{
Person(const std::string &name)
: name_(name)
{ }
std::string name_;
};
According to your specification, there are two flavours of Key: One that represents strings and must be initialized using a string, and another one that represents persons and must be initialized by a person pointer (I'll assume that the person-keys do not actually own these pointers, so you need to make sure the person objects they point to stay alive as long as the person-key exists).
We implement this by specializing Key into two derived classes, a PersonKey and a StringKey:
class PersonKey : public Key
{
public:
PersonKey(Person *person_ptr)
: Key() , person_ptr_(person_ptr)
{ }
virtual std::string repr() const
{
if (person_ptr_ != 0)
return std::string("Person/") + person_ptr_->name_;
else
return "<NUL>";
}
private:
Person *person_ptr_;
};
class StringKey : public Key
{
public:
StringKey(const std::string &str)
: Key() , str_(str)
{ }
virtual std::string repr() const
{
return str_;
}
private:
std::string str_;
};
When you make insertions into your map/multimap, you generate Key objects (which you represent as Key* or Key& or std::unique_ptr<Key>). When you want to insert a string, you generate them as StringKey objects, and when you want to insert them as person-pointers, you use PersonKey – but the data type of the key you insert will not reflect the specialization.
Here is an example of a general Key object (implemented as std::unique_ptr<Key>, but you may just use Key* if you are not afraid of memory leaks):
int main()
{
/* General key object: */
std::unique_ptr<Key> mykey;
/* Now it points to a string-key, initialized using
a string, as required: */
mykey.reset(new StringKey("hello"));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
/* Now the same key object is made to refer to
a person object: */
Person person("me");
mykey.reset(new PersonKey(&person));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
return 0;
}
Necessary headers for the code above are:
#include <iostream>
#include <memory>
#include <string>
(But memory is only required for my use of std::unique_ptr, which is not actually necessary to solve your problem.)
I think what you are really looking for are templates. Your solution with "root object" won't work as you can see with standard objects and external libraries but also you will not be able to use your containers with primitives (for example person id(as int) as key, and Person class as value).
With templates you can say what type you are going to work with at compile time and compiler will help you to obey your own rules. It can be declared like this:
template<class T1, class T2>
class Map{
T1 key;
T2 value;
(...)
}
Then you can use it more or less like this:
Map<std::String, int> phoneBook;
And compiler will guard you and warn, if you try to add, for example float instead of int, to you Map. But before you start coding I advice you to read some tutorials first, or maybe even some book on c++ in general. But if you want to start with generic right now, you can start her:
http://www.cplusplus.com/doc/tutorial/templates/
The only way you'd be able to store a string in your Object variable was if the string class inherited from your Object class, so you will have to implement your own String class unfortunately.
The real flaw here is that you are taking a Java/C# approach to design, where an Object variable can hold anything. In C++ the proper way to handle such things is through the use of templates, supposing your Map/Multimap/Node only need to hold one specific data type.
If your container needs to be able to hold any arbitrary data type, I would recommend using type erasure, although that can be a bit complicated for a beginner.
I am designing a Win32 library to parse the contents of the file (Columns and Values) and store it internally in a datastructure (Map). Now i need to expose API's so that the consumer can call those API's to get the results.
The file may have different formats eg FM1, FM2 etc. The consumer may query like
FM1Provider.GetRecords("XYZ");
FM2Provider.GetRecords("XYZ");
What i am planning to do is to have a CParser class that does all the parsing and expose the class.
CParser
{
bool LoadFile(string strFile);
Map<string,string> GetFM1Records(string key);
Map<string,string> GetFM1Records(string key);
};
or
class CResultProvider
{
virtual Map<string,string> GetRecords(string key)=0;
}
class CFM1ResultProvider : public CResultProvider
{
Map<string,string> GetRecords(string key);
}
class CFM2ResultProvider : public CResultProvider
{
Map<string,string> GetRecords(string key);
}
CParser
{
bool LoadFile(string strFile);
CResultProvider GetFM1ResultProvider();
CResultProvider GetFM1ResultProvider();
};
Please suggest me which one of these approaches are correct and scalable considering i am developing a library.
Your component seems to be dealing with two problems: parsing and storing. It is a good design practise to separate these into different components so that they can be used independently.
I would suggest you provide the parser only with callbacks for parsed data. This way the user of it can choose the most suitable container for her application, or may choose to apply and discard read data without storing.
E.g.:
namespace my_lib {
struct ParserCb {
virtual void on_column(std::string const& column) = 0;
virtual void on_value(std::string const& value) = 0;
protected:
~ParserCb() {} // no ownership through this interface
};
void parse(char const* filename, ParserCb& cb);
} // my_lib
BTW, prefer using namespaces instead of prefixing your classes with C.
Assuming the client would only have to call GetRecords once, and then work with the map, the first approach I prefer the first approach because it is simpler.
If the client has to reload the map in different places in his code, the second approach is preferable, because it enables the client to write his code against one interface (CResultProvider). Thus, he can easily switch the file format simply by selecting a different implementation (there should be exactly one place in his code where the implementation is chosen).