I have been developing a C++ software driver for the adc peripheral of the MCU.
The individual analog inputs connected to the adc can be configured for operation in the unipolar or bipolar mode. To reflect this fact in my design I have decided to model the analog inputs by the AnalogInput abstract class and then define two derived classes. UnipolarAnalogInput for the unipolar analog inputs and BipolarAnalogInput for the bipolar analog inputs. These two classes differ only in the implementation of the getValue() method.
enum class Type
{
Unipolar,
Bipolar
};
class AnalogInput
{
public:
virtual float getValue() = 0;
};
class UnipolarAnalogInput : public AnalogInput
{
public:
UnipolarAnalogInput(uint8_t _id, bool _enabled, Type _type);
bool isEnabled();
bool isReady();
float getValue();
private:
uint8_t id;
Type type;
bool enabled;
bool ready;
uint16_t raw_value;
};
class BipolarAnalogInput : public AnalogInput
{
public:
BipolarAnalogInput(uint8_t _id, bool _enabled, Type _type);
bool isEnabled();
bool isReady();
float getValue();
private:
uint8_t id;
Type type;
bool enabled;
bool ready;
uint16_t raw_value;
};
My goal is to fullfill following requirements:
work with both types of the analog inputs uniformly
have a chance to create either the instance of the UnipolarAnalogInput or BipolarAnalogInput
based on users configuration of the Adc which is known at the compile time
have a chance to create the instances in for loop iteration
have the implementation which is suitable for the embedded systems
Here are my ideas
As far as the requirement 1.
The ideal state would be to have AnalogInput analog_inputs[NO_ANALOG_INPUTS]. As far as I understand
correctly this is not possible in C++. Instead of that I need to define AnalogInput *analog_inputs[NO_ANALOG_INPUTS].
As far as the requirement 2.
It seems to me that the best solution for the other systems than the embedded systems would be to use the factory method design pattern i.e. inside the AnalogInput define
static AnalogInput* getInstance(Type type) {
if(type == Unipolar) {
// create instance of the UnipolarAnalogInput
} else if(type == Bipolar) {
// create instance of the BipolarAnalogInput
}
}
Here I would probably need to define somewhere auxiliary arrays for the UnipolarAnalogInput instances and the BipolarAnalogInput instances where the instances would be allocated by the factory method and the pointers to those arrays would be returned by the getInstance(). This solution seems to me to be pretty cumbersome due to the auxiliary arrays presence.
As far as the requirement 3.
for(uint8_t input = 0; input < NO_ANALOG_INPUTS; input++) {
analog_inputs[input] = AnalogInput::getInstance(AdcConfig->getInputType(input));
}
As far as the requirement 4.
Here I would say that what I have suggested above is applicable also for the embedded systems
because the solution avoids usage of the standard new operator. Question mark is the virtual
method getValue().
My questions is whether the auxiliary arrays presence is unavoidable?
The "auxiliary array" as you call it is mostly needed for memory management, i.e. you need to choose the memory to store your objects in. It's also an interface - the array is how you access the ADCs.
You can store your objects either in the heap or the (global) data segment - an array of objects implements the latter (you can also create global variables, one per ADC, which is a worse solution). If the compiler has all the information it needs to allocate the memory during compilation, it's usually the preferred approach. However - as you've noticed - polymorphism becomes rather annoying to implement with statically allocated objects.
The alternative is to keep them in heap. This is often totally acceptable in an embedded system if you allocate the heap memory at startup and keep it permanently (i.e. never try to release or re-use this part of heap, which would risk fragmentation). And this is really the only humane way to do polymorphic stuff, especially object instantiation.
If you don't like the array, use some other storage method - linked list, global variables, whatever. But you need to access the objects through a pointer (or a reference, which is also a pointer) for polymorhpism to work. And arrays are a simple concept, so why not use them?
Related
Let's assume this class hierarchy below.
class BaseClass {
public:
int x;
}
class SubClass1 : public BaseClass {
public:
double y;
}
class SubClass2 : public BaseClass {
public:
float z;
}
...
I want to make a heterogeneous container of these classes. Since the subclasses are derived from the base class I can make something like this:
std::vector<BaseClass*> container1;
But since C++17 I can also use std::variant like this:
std::vector<std::variant<SubClass1, SubClass2, ...>> container2;
What are the advantages/disadvantages of using one or the other? I am interested in the performance too.
Take into consideration that I am going to sort the container by x, and I also need to be able to find out the exact type of the elements. I am going to
Fill the container,
Sort it by x,
Iterate through all the elements, find out the type, use it accordingly,
Clear the container, then the cycle starts over again.
std::variant<A,B,C> holds one of a closed set of types. You can check whether it holds a given type with std::holds_alternative, or use std::visit to pass a visitor object with an overloaded operator(). There is likely no dynamic memory allocation, however, it is hard to extend: the class with the std::variant and any visitor classes will need to know the list of possible types.
On the other hand, BaseClass* holds an unbounded set of derived class types. You ought to be holding std::unique_ptr<BaseClass> or std::shared_ptr<BaseClass> to avoid the potential for memory leaks. To determine whether an instance of a specific type is stored, you must use dynamic_cast or a virtual function. This option requires dynamic memory allocation, but if all processing is via virtual functions, then the code that holds the container does not need to know the full list of types that could be stored.
A problem with std::variant is that you need to specify a list of allowed types; if you add a future derived class you would have to add it to the type list. If you need a more dynamic implementation, you can look at std::any; I believe it can serve the purpose.
I also need to be able to find out the exact type of the elements.
For type recognition you can create a instanceof-like template as seen in C++ equivalent of instanceof. It is also said that the need to use such a mechanism sometimes reveals poor code design.
The performance issue is not something that can be detected ahead of time, because it depends on the usage: it's a matter of testing different implementations and see witch one is faster.
Take into consideration that, I am going to sort the container by x
In this case you declare the variable public so sorting is no problem at all; you may want to consider declaring the variable protected or implementing a sorting mechanism in the base class.
What are the advantages/disadvantages of using one or the other?
The same as advantages/disadvantages of using pointers for runtime type resolution and templates for compile time type resolution. There are many things that you might compare. For example:
with pointers you might have memory violations if you misuse them
runtime resolution has additional overhead (but also depends how would you use this classes exactly, if it is virtual function call, or just common member field access)
but
pointers have fixed size, and are probably smaller than the object of your class will be, so it might be better if you plan to copy your container often
I am interested in the performance too.
Then just measure the performance of your application and then decide. It is not a good practice to speculate which approach might be faster, because it strongly depends on the use case.
Take into consideration that, I am going to sort the container by x
and I also need to be able to find out the exact type of the elements.
In both cases you can find out the type. dynamic_cast in case of pointers, holds_alternative in case of std::variant. With std::variant all possible types must be explicitly specified. Accessing member field x will be almost the same in both cases (with the pointer it is pointer dereference + member access, with variant it is get + member access).
Sending data over a TCP connection was mentioned in the comments. In this case, it would probably make the most sense to use virtual dispatch.
class BaseClass {
public:
int x;
virtual void sendTo(Socket socket) const {
socket.send(x);
}
};
class SubClass1 final : public BaseClass {
public:
double y;
void sendTo(Socket socket) const override {
BaseClass::sendTo(socket);
socket.send(y);
}
};
class SubClass2 final : public BaseClass {
public:
float z;
void sendTo(Socket socket) const override {
BaseClass::sendTo(socket);
socket.send(z);
}
};
Then you can store pointers to the base class in a container, and manipulate the objects through the base class.
std::vector<std::unique_ptr<BaseClass>> container;
// fill the container
auto a = std::make_unique<SubClass1>();
a->x = 5;
a->y = 17.0;
container.push_back(a);
auto b = std::make_unique<SubClass2>();
b->x = 1;
b->z = 14.5;
container.push_back(b);
// sort by x
std::sort(container.begin(), container.end(), [](auto &lhs, auto &rhs) {
return lhs->x < rhs->x;
});
// send the data over the connection
for (auto &ptr : container) {
ptr->sendTo(socket);
}
It's not the same. std::variant is like a union with type safety. No more than one member can be visible at the same time.
// C++ 17
std::variant<int,float,char> x;
x = 5; // now contains int
int i = std::get<int>(v); // i = 5;
std::get<float>(v); // Throws
The other option is based on inheritance. All members are visible depending on which pointer you have.
Your selection will depend on if you want all the variables to be visible and what error reporting you want.
Related: don't use a vector of pointers. Use a vector of shared_ptr.
Unrelated: I'm somewhat not of a supporter of the new union variant. The point of the older C-style union was to be able to access all the members it had at the same memory place.
Background:
Various modules of the program I'm involved with deal with the same combination of objects that are grouped together in an aggregating structure. There are well-known invariants imposed on that combination of objects, and those invariants are respected by all modules to the fullest extent. Each module is developed by a dedicated team, and each team needs their custom domain-specific methods to deal with that combination of objects.
Example:
To give you a tangible idea, imagine a sequence container class. The core of the container is the same across all users: it consists of data members for the storage, size/capacity and the allocator. But the set of methods, the contract and the body of those methods may vary a lot. One module may implement std-style operations, another module may implement all operations as nothrow methods, yet another module may insist on using their private checked iterators; some performance-critical module takes pain to ban all copy operations, while yet another module is all for making copies... Such requirements are well justified in each particular domain of any given module.
Speculations:
So, providing a single non-redundant set of methods which would satisfy the needs of all the client teams is impossible - requirements of some teams are mutually exclusive. Providing only those methods that are commonly required by all modules is rather useless, because the only common part is, probably, the destructor. Throwing together all possible implementations of all methods is not good either: poor maintainability and stability, confusingly bloated interface, lots of name clashes, lots of cross-module dependencies.
The question:
What options do I have to let several independent implementations operate on the same set of data members?
Things I tried:
Solutions I can see thus far aren't exactly nice, and I ain't entirely happy with any of them. I'll list them in answers, three approaches one by one.
A possible less-than-perfect solution to my own question:
2. Have a set of classes that operate on an external instance of core data by reference.
struct CoreData
{
int m_x;
~CoreData();
};
class TeamA
{
public:
// Allocates a new instance and owns it.
TeamA();
// Attaches an external instance without assuming ownership.
TeamA(CoreData& ext);
// Release the core, if must.
~TeamA();
void push_back(Whatever args);
Iter begin();
CoreData& GetCore();
private:
CoreData* m_core;
bool m_doIOwnThatCore;
};
class TeamB
{
public:
TeamB();
TeamB(CoreData& ext);
~TeamB();
int push_back(Whatever args);
CoreData& GetCore();
private:
CoreData* m_core;
bool m_doIOwnThatCore;
};
//--------------------- Usage:
void ServiceOfTeamA::CallServiceOfTeamB(ServiceOfTeamB* srv)
{
TeamA d;
srv->Process(d.GetCore());
d.begin();
}
void ServiceOfTeamB::Process(CoreData* core)
{
TeamB d(core);
d.push_back(567);
}
- What I don't like about this approach is that it imposes slight pessimization in terms of memory usage and performance. Also the syntax makes objects of type TeamA and TeamB look like values, while actually they have reference semantic.
+ Good news is that this approach allows somewhat better C++ syntax for the calls (but still, there's that ugly GetCore()), and meets RAII.
A possible less-than-perfect solution to my own question:
3. Throw the code on the mercy of de-facto defined behavior of reinterpret_cast.
// This is a standard layout class.
// It is the only class with data members;
// derived classes never append new data members.
class CoreData
{
public:
// Could be either explicit conversion function
// or implicit conversion operator.
template <typename Final>
// requires <LayoutCompatibleWithCore Final>
Final& As()
{
return reinterpret_cast<Final&>(*this);
}
protected:
~CoreData();
int m_x;
};
// No extra data members appended. No extra invariants imposed.
// This class is also a standard layout type,
// fully layout-compatible with CoreData.
class TeamA : public CoreData
{
public:
void push_back(Whatever args);
Iter begin();
};
class TeamB : public CoreData
{
public:
bool push_back(Whatever args);
X* begin();
};
//--------------------- Usage:
void ServiceOfTeamA::CallServiceOfTeamB(ServiceOfTeamB* srv)
{
TeamA d;
srv->Process(&d);
d.begin();
}
void ServiceOfTeamB::Process(CoreData* core)
{
TeamB& d = core->As<TeamB>();
d.push_back(567);
}
- However, such tricks are outlawed by the standard. So I have to decline this approach, too.
+ If it was legal, it would have offered the best syntax of the three, the syntax clearly showing the reference semantic, with RAII and no pessimization.
P.S. Invalidity of this approach puts me down. The whole point of layout compatibility seems to give the ability to communicate data between ABI-compatible processes or shared components. Too bad it doesn't allow communicating the data between parts of the same application...
A possible less-than-perfect solution to my own question:
1. Don't bother with methods and have freestanding functions instead.
struct CoreData
{
int m_x;
~CoreData();
};
void TeamA_init(CoreData& data);
void TeamA_push_back(CoreData& data, Whatever args);
Iter TeamA_begin(CoreData& data);
bool TeamB_init(CoreData& data, Other args);
bool TeamB_push_back(CoreData& data, Whatever args);
X* TeamB_begin(CoreData& data);
//--------------------- Usage:
void ServiceOfTeamA::CallServiceOfTeamB(ServiceOfTeamB* srv)
{
CoreData d;
TeamA_init(d);
srv->Process(&d);
TeamA_begin(d);
}
void ServiceOfTeamB::Process(CoreData* d)
{
TeamB_push_back(*d, 567);
}
- What I don't like about this approach is unfriendly syntax, no RAII and all data members being public. That's C, not C++.
+ On the bright side, this approach offers unlimited customization possibilities. No restrictions on choosing the right function for the task. No memory overhead, no runtime overhead (that is technically, the compiler have the same inlining and optimization opportunities as it would have would those free functions be methods).
I have been stumbling over this issue for a while now where I end up wanting to separate the data from the class I want to make and turn it into a pointer in the class.
Say for example I wanted to create an Item Class for an RPG game I keep trying to go:
class ItemTemplate
{
public:
enum TYPE { //Item types here. };
//ctor's and methods here.
private:
std::string m_name;
int m_buyprice;
int m_sellprice;
TYPE m_type;
int m_maxUses;
}
Basically the ItemTemplate is used to define any data that is constant for all instances of any Item object of that type like so:
const ItemTemplate cPotionTemplate( "Potion" , HEALING , 300 , 50 , 3 );
says all potions are called "Potion", are of the HEALING item types, cost 300G and sell for 50g and have 3 uses to start. None of that data is ever going to change. It would probably be more accurate for it to be HealingItemTemplate and to also say how much it recovers but that's getting off the point.
After that I want to create another class
class Item
{
public:
//ctors and methods here.
private:
ItemTemplate* m_Data;
int m_usesLeft;
}
Basically this just accesses the data in the ItemTemplate and tracks the number of uses the item still has.
What I am trying for is to cut down on the number of variables existing in memory when the program is running.
I know I could bundle all of this data into a single class but that would mean that every item would store a copy of data that doesn't or shouldn't change.
Taking sizeof(int) to be 4, sizeof(type) to be 4, sizeof(string) to be 4 and sizeof( a pointer ) to be 4.
The way I keep trying to implement it uses 8 bytes for each instance of an item but doing the bundled way would use 24 + ( m_name.capacity() or m_name.size() * sizeof(char) ) I know the latter doesn't accurately account for reserved space, but I'm not sure of the former.
Regardless, bundling all the data together in one class would use a minimum of 3x the number of bytes separating the data does. What I am struggling to understand is the downside of such an approach. My current thoughts are that it would be an increase in function calls and copies of data being made. I'm thinking that making the Item class a friend of the ItemTemplate class would be able to eliminate what I would consider a large portion of that increase in calls, those to the accessors.
Basically I'm just really struggling to fully understand the downside of the trade-off I keep wanting to make.
So what are the possible drawbacks to using such an implementation?
What methods exist to help determine when such an implementation is still worth using? If it matters for this I am using Code::Blocks 13.12 IDE but am woefully un-knowledgeable when it comes to using debuggers.
Is there another way to achieve this behavior that I'm missing?
I had considered templates but that seemed too rigid in terms of storing them as each derivation of the ItemTemplate class would create a new type Item<Derived Class> and no Item<type> would be able to be stored together unless they were from the same derivation. Which could work for some systems but isn't the desired implementation as it would make adding new Itemtypes much more of a chore.
An interface class in C++ would solve your concerns regarding the templates:
class ItemInterface
{
public:
enum TYPE { potion, scroll };
//ctor's and methods here.
public:
virtual std::string getName();
virtual int get_buyprice();
virtual int get_sellprice();
virtual TYPE get_type();
virtual int get_maxUses();
};
template<ItemInterface::TYPE T>
class item : ItemInterface {
private:
//Note, not all static members need to be constant
static const std::string m_name;
static const int m_buyprice;
static const int m_sellprice;
static const TYPE m_type;
static const int m_maxUses;
int m_usesLeft;
public:
item();
/*your getters implementation here*/
std::string getName(){return m_name;}
//etc...
};
//Your specialisations here.
//This looks ugly because it's private and const.
//Public static initialization would look better.
template<> const std::string item<ItemInterface::potion>::m_name = "potion";
//...
template<> const std::string item<ItemInterface::scroll>::m_name = "scroll";
You have identified two designs from the design patterns book.
Prototype pattern
Flyweight pattern
Prototype pattern
wikipedia prototype pattern
This assumes the construction of things can be achieved by defining available attributes and constructing a prototype. It simplifies the amount of subclassing needed, as the difference is only the parameters to the object.
Flyweight pattern
wikipedia : flyweight pattern
This pattern identifies that the state of many instanced objects may be split between "all these are the same" and these are the movable/usable attributes.
You are right to identify that these patterns have both advantages, and disadvantages, but whether they are appropriate, is based on your usage, not easy for people outside of your project to answer.
This question is not about the C++ language itself(ie not about the Standard) but about how to call a compiler to implement alternative schemes for virtual function.
The general scheme for implementing virtual functions is using a pointer to a table of pointers.
class Base {
private:
int m;
public:
virtual metha();
};
equivalently in say C would be something like
struct Base {
void (**vtable)();
int m;
}
the first member is usually a pointer to a list of virtual functions, etc. (a piece of area in the memory which the application has no control of). And in most case this happens to cost the size of a pointer before considering the members, etc. So in a 32bit addressing scheme around 4 bytes, etc. If you created a list of 40k polymorphic objects in your applications, this is around 40k x 4 bytes = 160k bytes before any member variables, etc. I also know this happens to be the fastest and common implementation among C++ compiles.
I know this is complicated by multiple inheritance (especially with virtual classes in them, ie diamond struct, etc).
An alternative way to do the same is to have the first variable as a index id to a table of vptrs(equivalently in C as below)
struct Base {
char classid; // the classid here is an index into an array of vtables
int m;
}
If the total number of classes in an application is less than 255(including all possible template instantiations, etc), then a char is good enough to hold an index thereby reducing the size of all polymorphic classes in the application(I am excluding alignment issues, etc).
My questions is, is there any switch in GNU C++, LLVM, or any other compiler to do this?? or reduce the size of polymorphic objects?
Edit: I understand about the alignment issues pointed out. Also a further point, if this was on a 64bit system(assuming 64bit vptr) with each polymorphic object members costing around 8 bytes, then the cost of vptr is 50% of the memory. This mostly relates to small polymorphics created in mass, so I am wondering if this scheme is possible for at least specific virtual objects if not the whole application.
You're suggestion is interesting, but it won't work if the executable is made of several modules, passing objects among them. Given they are compiled separately (say DLLs), if one module creates an object and passes it to another, and the other invokes a virtual method - how would it know which table the classid refers to? You won't be able to add another moduleid because the two modules might not know about each other when they are compiled. So unless you use pointers, I think it's a dead end...
A couple of observations:
Yes, a smaller value could be used to represent the class, but some processors require data to be aligned so that saving in space may be lost by the requirement to align data values to e.g. 4 byte boundaries. Further, the class-id must be in a well defined place for all members of a polymorphic inheritance tree, so it is likely to be ahead of other date, so alignment problems can't be avoided.
The cost of storing the pointer has been moved to the code, where every use of a polymorphic function requires code to translate the class-id to either a vtable pointer, or some equivalent data structure. So it isn't for free. Clearly the cost trade-off depends on the volume of code vs numer of objects.
If objects are allocated from the heap, there is usually space wasted in orer to ensure objects are alogned to the worst boundary, so even if there is a small amount of code, and a large number of polymorphic objects, the memory management overhead migh be significantly bigger than the difference between a pointer and a char.
In order to allow programs to be independently compiled, the number of classes in the whole program, and hence the size of the class-id must be known at compile time, otherwise code can't be compiled to access it. This would be a significant overhead. It is simpler to fix it for the worst case, and simplify compilation and linking.
Please don't let me stop you trying, but there are quite a lot more issues to resolve using any technique which may use a variable size id to derive the function address.
I would strongly encourage you to look at Ian Piumarta's Cola also at Wikipedia Cola
It actually takes a different approach, and uses the pointer in a much more flexible way, to to build inheritance, or prototype-based, or any other mechanism the developer requires.
No, there is no such switch.
The LLVM/Clang codebase avoids virtual tables in classes that are allocated by the tens of thousands: this work well in a closed hierachy, because a single enum can enumerate all possible classes and then each class is linked to a value of the enum. The closed is obviously because of the enum.
Then, virtuality is implemented by a switch on the enum, and appropriate casting before calling the method. Once again, closed. The switch has to be modified for each new class.
A first alternative: external vpointer.
If you find yourself in a situation where the vpointer tax is paid way too often, that is most of the objects are of known type. Then you can externalize it.
class Interface {
public:
virtual ~Interface() {}
virtual Interface* clone() const = 0; // might be worth it
virtual void updateCount(int) = 0;
protected:
Interface(Interface const&) {}
Interface& operator=(Interface const&) { return *this; }
};
template <typename T>
class InterfaceBridge: public Interface {
public:
InterfaceBridge(T& t): t(t) {}
virtual InterfaceBridge* clone() const { return new InterfaceBridge(*this); }
virtual void updateCount(int i) { t.updateCount(i); }
private:
T& t; // value or reference ? Choose...
};
template <typename T>
InterfaceBridge<T> interface(T& t) { return InterfaceBridge<T>(t); }
Then, imagining a simple class:
class Counter {
public:
int getCount() const { return c; }
void updateCount(int i) { c = i; }
private:
int c;
};
You can store the objects in an array:
static Counter array[5];
assert(sizeof(array) == sizeof(int)*5); // no v-pointer
And still use them with polymorphic functions:
void five(Interface& i) { i.updateCount(5); }
InterfaceBridge<Counter> ib(array[3]); // create *one* v-pointer
five(ib);
assert(array[3].getCount() == 5);
The value vs reference is actually a design tension. In general, if you need to clone you need to store by value, and you need to clone when you store by base class (boost::ptr_vector for example). It is possible to actually provide both interfaces (and bridges):
Interface <--- ClonableInterface
| |
InterfaceB ClonableInterfaceB
It's just extra typing.
Another solution, much more involved.
A switch is implementable by a jump table. Such a table could perfectly be created at runtime, in a std::vector for example:
class Base {
public:
~Base() { VTables()[vpointer].dispose(*this); }
void updateCount(int i) {
VTables()[vpointer].updateCount(*this, i);
}
protected:
struct VTable {
typedef void (*Dispose)(Base&);
typedef void (*UpdateCount)(Base&, int);
Dispose dispose;
UpdateCount updateCount;
};
static void NoDispose(Base&) {}
static unsigned RegisterTable(VTable t) {
std::vector<VTable>& v = VTables();
v.push_back(t);
return v.size() - 1;
}
explicit Base(unsigned id): vpointer(id) {
assert(id < VTables.size());
}
private:
// Implement in .cpp or pay the cost of weak symbols.
static std::vector<VTable> VTables() { static std::vector<VTable> VT; return VT; }
unsigned vpointer;
};
And then, a Derived class:
class Derived: public Base {
public:
Derived(): Base(GetID()) {}
private:
static void UpdateCount(Base& b, int i) {
static_cast<Derived&>(b).count = i;
}
static unsigned GetID() {
static unsigned ID = RegisterTable(VTable({&NoDispose, &UpdateCount}));
return ID;
}
unsigned count;
};
Well, now you'll realize how great it is that the compiler does it for you, even at the cost of some overhead.
Oh, and because of alignment, as soon as a Derived class introduces a pointer, there is a risk that 4 bytes of padding are used between Base and the next attribute. You can use them by careful selecting the first few attributes in Derived to avoid padding...
The short answer is that no, I don't know of any switch to do this with any common C++ compiler.
The longer answer is that to do this, you'd just about have to build most of the intelligence into the linker, so it could coordinate distributing the IDs across all the object files getting linked together.
I'd also point out that it wouldn't generally do a whole lot of good. At least in a typical case, you want each element in a struct/class at a "natural" boundary, meaning its starting address is a multiple of its size. Using your example of a class containing a single int, the compiler would allocate one byte for the vtable index, followed immediately by three byes of padding so the next int would land at an address that was a multiple of four. The end result would be that objects of the class would occupy precisely the same amount of storage as if we used a pointer.
I'd add that this is not a far-fetched exception either. For years, standard advice to minimize padding inserted into structs/classes has been to put the items expected to be largest at the beginning, and progress toward the smallest. That means in most code, you'd end up with those same three bytes of padding before the first explicitly defined member of the struct.
To get any good from this, you'd have to be aware of it, and have a struct with (for example) three bytes of data you could move where you wanted. Then you'd move those to be the first items explicitly defined in the struct. Unfortunately, that would also mean that if you turned this switch off so you have a vtable pointer, you'd end up with the compiler inserting padding that might otherwise be unnecessary.
To summarize: it's not implemented, and if it was wouldn't usually accomplish much.
I have a design question that has been bugging me for a while but I cannot find a good (in a OOP sense) solution for this. The language is C++ and I keep coming back to RTTI - which is often referred to as an indicator for bad design.
Suppose we have a set of different kinds of modules implemented as different classes. Each kind of module is characterized by a defined interface, however the implementation may vary.
Thus my first idea was to create an interface (pure abstract) class for each kind of module (e.g. IModuleFoo, IModuleBar etc.) and the implementations in seperate classes. So far so good.
class IModuleFoo {
public:
virtual void doFoo() = 0;
};
class IModuleBar {
public:
virtual void doBar() = 0;
};
On the other hand we have a set of (application) classes and each of them uses a couple of those modules but only through the interfaces - even the modules themselves might use other modules. However, all of the application classes will share the same pool of modules. My idea was to create a manager class (ModuleManager) for all modules which application classes can query for the module types they need. The available modules (and the concrete implementation) are set up during initialization of the manager and may vary over time (but that is not really part of my question).
Since the number of different module kinds is most probably >10 and may increase over time it does not appear suitable to me to store references (or pointers) to them separately. In addition there might be a couple of functions the manager needs to invoke on all managed modules. Thus I created another interface (IManagedModule) with the benefit that I can now use a container (list, set, whatsoever) of IManagedModules to store them in the manager.
class IManagedModule {
public:
virtual void connect() = 0;
{ ... }
};
The consequence is that a module that shall be managed needs to inherit both from the IManagedModule and from the appropriate interface for its type.
But things turn ugly when I think about the ModuleManager. It can be assumed that there is at most one instance of each module type present at each time. Thus if it was possible to do something like this (where manager is the instance of the ModuleManager), everything would be fine:
IModuleFoo* pFoo = manager.get(IModuleFoo);
But I'm pretty sure that it's not. I also thought about a template based solution like:
IModuleFoo* pFoo = manager.get<IModuleFoo>();
That could work but I have no idea how to find the right module within the manager if all I have is a set of IManagedModules - that is without the use of RTTI, of course.
One approach would be to provide IManagedModule with a virtual getId() method, rely on the implementations to use non-ambigous ids for each kind of module and do the pointer casting on your own. But that's just reinventing the wheel (namely RTTI) and requires a lot of discipline within the implementing classes (providing the right ids etc...) which is not desirable.
Long story short - the question is if there is really no way around some kind of RTTI here and in this case RTTI might even be a valid solution or if there might be a better (cleaner, safer, ...) design which exhibits the same flexibility (e.g. loose coupling between application classes and module classes...)? Did I miss anything?
It sounds like you're looking for something similar to COM's QueryInterface. Now, you don't need to implement COM entirely, but the basic principle stands: You have a base class, with a virtual function, to which you pass an identifier specifying which interface you want. The virtual function then looks to see if it can implement that interface, and if so, passes back a pointer to that interface.
For example:
struct IModuleBase {
// names changed so as not to confuse later programmers with true COM
virtual bool LookupInterface(int InterfaceID, void **interfacePtr) = 0;
// Easy template wrapper
template<typename Interface>
Interface *LookupInterface() {
void *ptr;
if (!LookupInterface(Interface::INTERFACE_ID, &ptr)) return NULL;
return (Interface *)ptr;
}
};
struct IModuleFoo : public IModuleBase {
enum { INTERFACE_ID = 42 };
virtual void foo() = 0;
};
struct SomeModule : public IModuleFoo {
virtual bool LookupInterface(int interface_id, void **pPtr) {
switch (interface_id) {
case IModuleFoo::INTERFACE_ID:
*pPtr = (void*)static_cast<IModuleFoo *>(this);
return true;
default:
return false;
}
}
virtual void foo() { /* ... */ }
};
It's a bit unwieldy, but it's not too bad, and without RTTI you don't have much of a choice besides an approach like this.
I think bdonlan's suggestion is good, but requiring each module type to declare a distinct INTERFACE_ID is a maintenance headache. The distinctness can be accomplished automatically by having each module type declare a static object and using its address as the ID:
struct IModuleFoo : public IModuleBase {
static char distinct_; // Exists only to occupy a unique address
static const void *INTERFACE_ID;
virtual void foo() = 0;
};
// static members need separate out-of-class definitions
char IModuleFoo::distinct_;
const void *IModuleFoo::INTERFACE_ID = &distinct_;
In this case we are using void * as the interface ID type, instead of int or an enumerated type, so the types in some other declarations will need to change.
Also, due to quirks in C++, the INTERFACE_ID values, despite being labelled const, are not "constant enough" to be used for case labels in switch statements (or array size declarations, or a handful of other places), so you would need to change the switch statement to an if. As described in section 5.19 of the standard, a case label requires an integral constant-expression, which roughly speaking is something the compiler can determine just from looking at the current translation unit; while INTERFACE_ID is a mere constant-expression, whose value cannot be determined until link time.