My colleague and I are implementing Google Test for our code base and are running into a few issues with Contravariance regarding using standard template containers.
So, Google Test requires us to make a pure virtual Interface class that mirrors our actual class which will inherit the interface and implement all virtual functions. This is to be used in Google Mock for testing. This is a strict requirement to work too, otherwise we need to add templates to all of our classes which will only be of one type... this seems rather unintuitive just to get the test code working.
So we just investigated some code that exhibits the behavior of the issue:
#include <vector>
#include <string>
#include <iostream>
class Fruit{
public:
Fruit(std::string colorIn) : color(colorIn) {}
std::string color;
};
class Apple : public Fruit{
public:
Apple() : Fruit("Red"){ appleType = "Honey Crisp"; }
Apple(const Fruit& fruit) : Fruit(fruit.color) { appleType = "Honey Crisp"; }
std::string appleType;
};
class Banana : public Fruit{
public:
Banana() : Fruit("Yellow"){ bananaType = "Dole"; }
Banana(const Fruit& fruit) : Fruit(fruit.color) { bananaType = "Dole"; }
std::string bananaType;
};
void takeMyFruit(std::vector<Fruit>& fruits){
if(!fruits.empty()){
std::cout << "Take my " << fruits[0].color << " " << ((Banana)(fruits[0])).bananaType << " banana." << std::endl;
std::cout << "Take my " << fruits[1].color << " " << ((Apple)(fruits[1])).appleType << " apple." << std::endl;
}else{
std::cout << "You gave me an empty bag?" << std::endl;
}
}
int main(){
std::vector<Fruit> fruits;
fruits.push_back(Banana());
fruits.push_back(Apple());
std::vector<Banana> bananas = { Banana() };
std::vector<Apple> apples = { Apple() };
takeMyFruit(fruits); //Why can I do this?
//takeMyFruit(bananas); //Compile error due to contravariance
//takeMyFruit(apples); //Compile error due to contravariance
return 0;
}
We need to be able to compile something that can take a base type for a container, i.e. std::vector<BaseType>, but we are only populating it with one single DerivedType.
Why would we be allowed to mix two different derived types in the std::vector<Fruit> in the above code example we created, (i.e. Apple and Banana), but not be able to pass a std::vector<DerivedType> to a functional parameter that accepts std::vector<BaseType>?
What would be the best way to get around this issue regarding Google Test and Google Mock. They say that if production code is being changed to suit the need for tests, then it probably isn't the best practice.
Another way to do this that we saw was to add templates for the derived types to any class that defines them as members. Doing this would be a rather large rehaul and would then require any user of the library we're creating to have to wrap every instantiation of these new classes that hold these interface/derived types just to get Google Mock to work.
We're operating on legacy code at this point that can't be changed this much to incorporate Google Mock. We can't just skip testing these new class types either, what would be the best method for moving forward?
Please see the code below.
#include <vector>
#include <string>
#include <iostream>
#include <memory>
using namespace std;
class Fruit
{
public:
Fruit(std::string colorIn) : color(colorIn) { }
std::string color;
};
class Apple : public Fruit{
public:
Apple() : Fruit("Red") { appleType = "Honey Crisp"; }
Apple(const Fruit& fruit) : Fruit(fruit.color) { appleType = "Honey Crisp"; }
std::string appleType;
};
class Banana : public Fruit{
public:
Banana() : Fruit("Yellow") { bananaType = "Dole"; }
Banana(const Fruit& fruit) : Fruit(fruit.color) { bananaType = "Dole"; }
std::string bananaType;
};
void takeMyFruit(std::vector<shared_ptr<Fruit>>& fruits)
{
if (!fruits.empty())
{
for (vector<shared_ptr<Fruit>>::const_iterator it = fruits.begin(); it != fruits.end(); ++it)
std::cout << "Take my " << (*it)->color;
// You shouldn't use the following two lines as you don't know what is in the vector.
// std::cout << "Take my " << fruits[0]->color << " " << std::dynamic_pointer_cast<Banana>(fruits[0])->bananaType << " banana." << std::endl;
// std::cout << "Take my " << fruits[1]->color << " " << std::dynamic_pointer_cast<Apple>(fruits[1])->appleType << " apple." << std::endl;
}
else
{
std::cout << "You gave me an empty bag?" << std::endl;
}
}
int main()
{
std::vector<std::shared_ptr<Fruit>> fruits;
fruits.push_back(std::make_shared<Banana>());
fruits.push_back(std::make_shared<Apple>());
std::vector<std::shared_ptr<Fruit>> bananas = { std::make_shared<Banana>() };
std::vector<std::shared_ptr<Fruit>> apples = { std::make_shared<Apple>() };
takeMyFruit(fruits); //Why can I do this?
takeMyFruit(bananas); //OK now
takeMyFruit(apples); //OK now
return 0;
}
If you want to have a functionality to show the fruit type, the correct way is to add a virtual function in Fruit
virtual string FruitDetailType() = 0;
and implement it in the derived class - lets say, apple class
virtual string FruitDetailType()
{
return "Apple, Honey Crisp";
}
//or,
virtual string FruitDetailType()
{
return appleType;
}
Related
#include <iostream>
#include <string>
#include <vector>
//#include "V2d.h"
using namespace std;
class item {
public:
int value;
string description;
item() {}
virtual ~item() {}
virtual void display() {
cout << "Value: " << value << endl;
}
};
class Cat : public item {
public:
string name = "Cat";
Cat() {
value = 20;
description = "a cat";
}
void display() {
cout << "Name: " << name << endl;
cout << "Value: " << value << endl;
cout << "Description: " << description << "\n" << endl;
}
};
class Dog : public item {
public:
string name = "Dog";
Dog() {
value = 10;
description = "a dog";
}
void display() {
cout << "Name: " << name << endl;
cout << "Value: " << value << endl;
cout << "Description: " << description << "\n" << endl;
}
};
class v2d {
public:
int hitPoints;
enum ItemName {
APPLE, ORANGE
};
vector<item*> inventory;
v2d() {
}
~v2d() {
for (int i = 0; i < inventory.size(); i++) {
delete inventory[i];
}
inventory.clear();
}
v2d(const v2d& orig) : inventory(orig.inventory.size()) {
hitPoints = orig.hitPoints;
for (int i = 0; i < inventory.size(); ++i) {
inventory[i] = new item(*orig.inventory[i]);
}
cout << "Copied!" << endl;
}
void display() {
for (int i = 0; i < inventory.size(); i++) {
inventory[i]->display();
}
}
};
int main() {
v2d vect1;
vect1.inventory.push_back(new Cat());
vect1.inventory.push_back(new Dog());
vect1.inventory.push_back(new Dog());
vect1.inventory.push_back(new Cat());
vect1.inventory.push_back(new Dog());
vect1.display();
cout << "**************************\n" << endl;
v2d vect2(vect1);
vect2.display();
}
And where would I start with trying to overload the += and -= operators in v2d to subtract and add to the inventory vector?
I have gotten to the part of having the base class and the derived classes; I seem to be having trouble not using dynamic casting. Is there a simple way to deep copy a vector of derived class pointers via the copy constructor without using a clone() function?
To do this correctly two more things need to be added to the base class:
class item {
public:
virtual ~item() {}
The base class must have a virtual destructor.
virtual item *clone() const=0;
And an abstract method that's traditionally called clone(). Every one of your subclasses must implement clone(), typically by using a copy constructor:
class Cat : public item {
public:
item *clone() const override { return new Cat{*this}; };
The same thing is done for all other subclasses of item. Then, with this in place you can clone a vector of these objects, correctly:
for (int i = 0; i < inventory.size(); ++i) {
inventory[i] = orig.inventory[i]->clone();
}
I seem to be having trouble not using dynamic casting. Is there a simple way to deep copy a vector of derived class pointers via the copy constructor without using a clone() function?
clone is the traditional solution, but I will propose an alternative.
Consider using type erasure and value types. It will give the simplest use, by far, with more complexity to set it up. Value types generally interoperate more cleanly with a lot of the language and library. They are less intrusive. Standard types can satisfy your interfaces. There is no inheritance required. Value types do not require pointers and indirection everywhere. Those are but a few of the advantages.
With C++20, we can define a concept for item, which is an interface by another name:
template <typename Item>
concept item = std::copyable<Item> && requires(const Item& const_item, Item& item) {
{ item.value() } -> std::same_as<int&>;
{ const_item.value() } -> std::same_as<const int&>;
{ item.description() } -> std::same_as<std::string&>;
{ const_item.description() } -> std::same_as<const std::string&>;
{ item.display() };
{ const_item.display() };
};
Prior to C++20, concepts were generally implicit or documented, but not entities in code. But in C++20, we can define them. Note that this formulation requires std::copyable so any item satisfying this will be copyable, using the standard copy constructor. Also, note that raw data members in an interface complicate things slightly, so I replaced them with accessors while still allowing the public read and write access the code in the question allows.
With the interface defined, one can then define a type-erased version: define a type any_item satisfying item which can itself hold a value of any type satisfying item. You will likely find many online sources about type erasure, and the choices involved. The key is to have storage for the object, and a table of function pointers for the interface (a vtable). One can store the object on the heap or have a small internal buffer for small objects. One can store the vtable inline or store a pointer to the vtable. One can write the vtable explicitly, or rely on a hidden class hierarchy to coerce the compiler into writing one. One can even rely on a library (e.g. dyno) to do this for you.
Note that all this complexity is handled by the interface or library author. The user does not have to inherit anything. The user does not have to define boilerplate clone functions. The user only has to define the functions the concept requires. Then the user can use the type-erased type, and use it like any other value type. The user can put them in a std::vector and watch the copy constructor just work.
Code that looked like:
std::vector<item*> inventory;
~v2d()
{
for (auto* item: inventory) {
delete item;
}
}
v2d(const v2d& orig) :
inventory(orig.inventory.size())
{
for (int i = 0; i < inventory.size(); i++) {
inventory[i] = new item(*orig.inventory[i]);
}
cout << "Copied!" << endl;
}
vect1.inventory.push_back(new Cat());
Becomes:
// just use values
std::vector<any_item> inventory;
// destruction just works
~v2d() = default;
// copy just works
v2d(const v2d& other) :
inventory(other.inventory)
{
std::cout << "Copied!\n";
}
// just use values, again
vect1.inventory.push_back(cat());
See an example
Is the use of new/delete appropriate here? Or any way to avoid them?
The point: class myOptions need an instance of Submarine. The Parameter are known at the time the myOptions-Construcor is called.
#include <iostream>
#include <string>
class Submarine
{
public:
Submarine(std::string o)
{
std::cout << "create class Submarine: " << o << "\n";
}
~Submarine(){};
};
class myOptions
{
public:
myOptions(std::string o):option(o)
{
std::cout << "create class myOption: " << option << "\n";
submarine = new Submarine(option);
}
~myOptions()
{
delete submarine;
}
private:
std::string option{};
Submarine *submarine;
};
int main(void){
myOptions{"help"};
}
You don't need to use dynamic memory here:
class myOptions
{
public:
myOptions(std::string o):option(o), submarine(o)
{
std::cout << "create class myOption: " << option << "\n";
}
private:
std::string option{};
Submarine submarine;
};
If you really need to use dynamic memory allocation (and you are not allowed to use smart pointers), you must apply the rule of five to your class. Currently it will blow up your program anytime you decide to pass it by value.
I was writing some exercise code to understand class inheritance and I couldn't figure out If there is a way to do what I tried to explain in the title.
So what I want to do to have Family and Member (family members) class. A family can have multiple members but members can have only one family. But let's say one of the members got married. So they are changing their family from A to other family B. Is there a way to do that?
#include <iostream>
#include <string>
class Family {
std::string _familyName;
public:
Family();
void setFamilyName(std::string& str) { _familyName = str;}
void printFamilyName() {
std::cout << "Family name is: " << _familyName << std::endl;
}
}
class Member : public Family {
std::string _firstName;
public:
Member();
void setFirstName(std::string& str) { _firstName = str;}
void changeFamily(Family *f) {} // What do i do here ?
}
int main(){
Member m;
m.setFamilyName("Smith");
m.setFirstName("John");
m.printFamilyName();
Family f;
B.setFamilyName("Williams");
m.changeFamily(&f);
m.printFamilyName();
return 0;
}
From this, I would like to get an output as
Family name is: Smith
Family name is: Williams
Thank you.
(An extra question is, is there a way to create a member variable without constructing the family part, but to bind the new member varible to an existing family variable while declaring the member ?)
A family can have multiple members but members can have only one family. But let's say one of the members got married. So they are changing their family from A to other family B. Is there a way to do that?
You need the relationship defined between objects, not the object types. You can do that by using a container-contained relationship between objects, not a base class - derived class relationship between the classes.
struct Member;
struct Family
{
std::vector<Member*> members;
}
struct Member
{
Family* family;
}
I'll let you figure out how define the member functions of the classes so the relationships between the objects and the lifetimes of the objects are maintained in a robust manner.
I don't think you need inheritance, and what you perceive to be inheritance is not correct. When working with multiple classes that work together either it be one class containing another or parent child relationship, there is always one question to ask yourself: The relationship between the two classes is it a "Is a relationship" or is it "Has a relationship". When you ask yourself that question and it is very easy to answer then you should know what design type your classes will need.
For example: A dog is a mammal and A dog has paws. So if we have 3 classes here. The structure would look something like this:
class Mammal {
/* code */
};
class Dog : public Mammal {
/* code */
};
Here we have inheritance because a Dog IS A Mammal and will inherit all of the traits that all Mammals have!
Next we would have this:
class Paws {
/* code */
};
So let's go back to our "dog" class
class Dog : public Mammal {
private:
Paws paws; // this is a member of Dog because a Dog HAS PAWS!
};
I hope this clears things up with the basic structure of classes!
Now to look back at your initial problem, here is what I have done. I made a basic struct to represent a FamilyMember where they all have the same information about each other in common. I abstracted out the last name completely to remove any dependencies and having to check if the last name is the same and to update or replace it etc. My code looks like this:
struct FamilyMember {
std::string firstName_;
unsigned int age_;
char gender_;
FamilyMember() = default;
FamilyMember(const std::string& firstName, unsigned int age, char gender) :
firstName_(firstName),
age_(age),
gender_(gender)
{}
};
class Family {
private:
std::string familyName_;
std::vector<FamilyMember> members_;
public:
Family() = default;
explicit Family(const std::string& familyName) : familyName_( familyName ) {
}
void addMember(FamilyMember& member) {
members_.push_back(member);
}
FamilyMember& getMember(unsigned int idx) {
return members_.at(idx);
}
std::vector<FamilyMember>& getFamily() {
return members_;
}
const std::string& getFamilyName() const { return familyName_; }
};
int main() {
Family Smith("Smith");
FamilyMember John("John", 32, 'M');
FamilyMember Sarah("Sarah", 29, 'F');
FamilyMember Amanda("Amanda", 19, 'F');
Smith.addMember(John);
Smith.addMember(Sarah);
Smith.addMember(Amanda);
std::cout << "Meet the " << Smith.getFamilyName() << "s:\n";
for (auto& m : Smith.getFamily()) {
std::cout << m.firstName_ << " " << Smith.getFamilyName() << " " << m.age_ << " " << m.gender_ << '\n';
}
Family Jones("Jones");
FamilyMember Tom("Tom", 44, 'M');
FamilyMember Mary("Mary", 43, 'F');
FamilyMember Mike("Mike", 21, 'M');
Jones.addMember(Tom);
Jones.addMember(Mary);
Jones.addMember(Mike);
std::cout << "Meet the " << Jones.getFamilyName() << "s:\n";
for (auto& m : Jones.getFamily() ) {
std::cout << m.firstName_ << " " << Jones.getFamilyName() << " " << m.age_ << " " << m.gender_ << '\n';
}
std::cout << "We present to you today the Union between: "
<< Jones.getMember(2).firstName_ << " " << Jones.getFamilyName() << " and "
<< Smith.getMember(2).firstName_ << " " << Smith.getFamilyName() << '\n';
Jones.addMember(Amanda);
std::cout << "We now have Mr. " << Jones.getMember(2).firstName_ << " " << Jones.getFamilyName() << " and "
<< "Mrs. " << Jones.getMember(3).firstName_ << " " << Smith.getFamilyName() << " " << Jones.getFamilyName() << '\n';
return 0;
}
So as you can see from the small program above; we do not have the dependency of having to modify the last name at all. With this kind of structure, when "Amanda" marries "Mike". She still belongs to the "Smith" family, but she has also joined with the "Jones" family. Now if you try to access her individual data through the structure, there is no information about her last name. You can retrieve her first name, age and gender. If you want the information for the last name, you have to retrieve that from the Family class itself as it never changes!
Short answer: No. Inheritance is set at compile time, you can't change it at runtime.
But I don't think inheritance is what you're looking for. Member inherits all the members and functions of Family. It doesn't imply a relationship between that Member and any particular instance of Family. This is reflected in your design - _familyName is a string, not a reference to an object or something. But you can have references to other objects. For example, like this:
#include <iostream>
#include <string>
#include <vector>
class Family {
std::string _familyName;
public:
Family(std::string name) : _familyName(name) {};
void printFamilyName() {
std::cout << "Family name is: " << _familyName << std::endl;
}
}
class Member {
std::string _firstName;
std::vector<Family*> families;
public:
Member(Family *familyPtr) { addFamily(familyPtr) };
void setFirstName(std::string& str) { _firstName = str;}
void addFamily(Family *familyPtr) { families.push_back(familyPtr) };
void printFamilies() { for (auto &ptr : families) { ptr->printFamilyName() } };
}
int main()
{
Family Jackson("Jackson");
Family Williams("Williams");
Member John(&Jackson);
Member Alice(&Williams);
John.printFamilies();
John.addFamily(&Williams);
John.printFamilies();
return 0;
}
Member doesn't need any of the functions or values of class Family, so we don't inherit them. Instead we maintain a list of pointers to Family objects that this Member "belongs" to, thus implying a relationship between this Member and those instances of Family. When we want to print all the Family objects that Member owns, we just loop through the vector of pointers and call printFamilyName() on each of them.
Within JavaScript, you can pull off something like this:
function bunny() { alert("The bunny jumped."); }
var oldBunny = bunny;
function bunny() {
oldBunny();
alert("The bunny also ran.");
}
bunny(); // The bunny Jumped. The bunny also ran.
As one can see, the old "bunny" function had code appended to it by copying to a variable, then recreating the function with the same name. The copy of the original function runs, and the new code also runs.
I wish to replicate a similar mechanic in C++.
Now before you have a meltdown and start explaining the differences between static and dynamic languages, I get it. I'm not looking for something identical to what's provided, but I do desire something similar.
Furthermore, I'm not trying to do this to modify existing code; I wish to format my own source code to allow such a mechanic for other users to take advantage of.
One of the first ideas I had was to perhaps setup various macros within the code that could later be modified by other files.
Another idea would be to create a Signal and Slots system like in QT. Though I have no clue how to do such a thing myself.
Thank you for reading; I hope you have some suggestions.
Well, if you recognize which feature of JavaScript functions makes this possible, it's not too hard to do the same in C++. In JavaScript functions also have closures, which regular function in C++ don't have. But C++ lambdas are of a closure type. And if one defines bunny to be something which can both hold an object of a closure type, and be reassigned, you're all set.
The C++ standard library offers a nice default choice for this, in the form of std::function. We can just re-write your original JavaScript as follows:
std::function<void()> bunny = [] {
std::cout << "The bunny jumped.\n";
};
auto oldBunny = std::move(bunny);
bunny = [oldBunny] {
oldBunny();
std::cout << "The bunny also ran.\n";
};
bunny();
You can use functors.
#include <iostream>
#include <string>
class Base
{
public:
virtual std::string operator ()()
{
return "Base call";
}
virtual ~Base() {}
};
class Derived : public Base
{
public:
virtual std::string operator()()
{
return "Wrapper: " + Base::operator()();
}
};
int main()
{
Base* pFun = new Base;
std::cout << "Now check Base: " << (*pFun)() << std::endl;
delete pFun;
pFun = new Derived;
std::cout << "Now check Derived: " << (*pFun)() << std::endl;
return 0;
}
Assuming the goal is to allow the calling code to extend the program's functionality beyond what the initial code provided, I might use a user-updatable array of functor-objects, something like this:
#include <iostream>
#include <memory>
class Function
{
public:
virtual void Call() = 0;
};
typedef std::shared_ptr<Function> FunctionSharedPointer;
class OldBunny : public Function
{
public:
virtual void Call()
{
std::cout << "The bunny jumped." << std::endl;
}
};
class NewBunny : public Function
{
public:
NewBunny(FunctionSharedPointer oldFunction) : _oldFunction(oldFunction) {/* empty */}
virtual void Call()
{
_oldFunction->Call();
std::cout << "The bunny also ran." << std::endl;
}
private:
FunctionSharedPointer _oldFunction;
};
enum {
FUNCTION_BUNNY,
// other functions could be declared here later...
NUM_FUNCTIONS
};
// Our table of functions that the user can Call() if he wants to
static FunctionSharedPointer _functionTable[NUM_FUNCTIONS];
// Wrapper function, just to keep users from accessing our table directly,
// in case we ever want to change it to something else
void CallFunction(int whichFunction)
{
_functionTable[whichFunction]->Call();
}
// Another wrapper function
void SetFunction(int whichFunction, FunctionSharedPointer newFunctionDefinition)
{
_functionTable[whichFunction] = newFunctionDefinition;
}
// And another
FunctionSharedPointer GetFunction(int whichFunction)
{
return _functionTable[whichFunction];
}
int main(int argc, char ** argv)
{
// Our default function values get set here
SetFunction(FUNCTION_BUNNY, std::make_shared<OldBunny>());
std::cout << "before:" << std::endl;
CallFunction(FUNCTION_BUNNY);
// Now let's update an entry in our function table to do something different!
FunctionSharedPointer op = GetFunction(FUNCTION_BUNNY);
FunctionSharedPointer np = std::make_shared<NewBunny>(op);
SetFunction(FUNCTION_BUNNY, np);
std::cout << "after:" << std::endl;
CallFunction(FUNCTION_BUNNY);
return 0;
}
void bunny()
{
cout << "The bunny jumped." << endl;
}
void oldBunny()
{
bunny();
}
void newBunny()
{
bunny();
cout << "The bunny also ran." << endl;
}
#define bunny newBunny
int main()
{
bunny();
return 0;
}
If you don't need oldBunny(), just remove it.
I am attempting to use static polymorphism to create a decorator pattern.
As to why I do not use dynamic polymorphism, please see this QA. Basically, I could not dynamic_cast to each decorator so as to access some specific functionality present only in the decorators (and not in the base class A).
With static polymorphism this problem has been overcome, but now I cannot register all the et() methods from the decorators back to the base class A (as callbacks or otherwise), thus when A::et() gets called, only A::et() and Z::et() get executed. I want all of A,X,Y,Z ::et() to be executed (the order for X,Y,Z does not matter).
How can I do that using the following structure?
I can see in wikipedia that CRTP should allow you to access member of a derived class using static_cast, but how do you approach the problem when there are multiple derived template classes?
If this is not possible with static polymorphism but it is possible with dynamic polymorphism could you reply to the other question?
struct I {
virtual void et() = 0;
};
class A : public I {
public:
A() {
cout << "A::ctor " ;
decList.clear();
}
void regDecorator(I * decorator)
{
if (decorator) {
cout << "reg= " << decorator << " ";
decList.push_back(decorator);
}
else
cout << "dec is null!" <<endl;
}
virtual void et()
{
cout << "A::et ";
cout << "declist size= " << decList.size() << endl;
list<I*>::iterator it;
for( it=decList.begin(); it != decList.end(); it++ )
static_cast<I *>(*it)->et();
}
std::list<I*> decList; //FIXME
};
template<typename Base>
class X: public Base {
public:
X(){
cout << "X::ctor ";
Base::regDecorator(this);
}
virtual void et(){
cout << "X::et" <<endl;
}
};
template<typename Base>
class Y: public Base {//public D {
public:
Y(){
cout << "Y::ctor ";
Base::regDecorator(this);
}
void et(){
cout << "Y::et" <<endl;
}
};
template<typename Base>
class Z: public Base {//public D {
public:
Z() {
cout << "Z::ctor ";
Base::regDecorator(this);
}
void et(){
cout << "Z::et" <<endl;
}
};
int main(void) {
Z<Y<X<A> > > mlka;
cout << endl;
mlka.et();
return 0;
}
This structure is to be used as a reference for data acquisition from a set of sensors. class A is the base class and contains common functionality of all the sensors. This includes:
- data container (f.e. `boost::circular_buffer`) to hold an amount of timestamped sample data acquired from the sensor.
- a Timer used to measure some timed quantities related to the sensors.
- other common data and calculation methods (fe. `calculateMean()`, `calculateStdDeviation()`)
In fact the A::timer will call A::et() on completion in order to perform some statistical calculations on the sampled data.
Similarly, X,Y,Z are types of sensor objects each with responsibility to extract different type of information from the sampled data. and X,Y,Z::et() perform a different type of statistical calculation on the data. The aim is perform this calculation as soon as the A::Timer waiting time elapses. This is why I want to have access to all of X,Y,Z::et() from A::et(). Is it possible without affecting the static polymorphism shown in the example?
Thank you
You started using mixins, so use them to the end.
It follows a minimal, working example:
#include<iostream>
struct I {
virtual void et() = 0;
};
template<typename... T>
struct S: I, private T... {
S(): T{}... {}
void et() override {
int arr[] = { (T::et(), 0)..., 0 };
(void)arr;
std::cout << "S" << std::endl;
}
};
struct A {
void et() {
std::cout << "A" << std::endl;
}
};
struct B {
void et() {
std::cout << "B" << std::endl;
}
};
int main() {
I *ptr = new S<A,B>{};
ptr->et();
delete ptr;
}
As in the original code, there is an interface I that offers the virtual methods to be called.
S implements that interface and erases a bunch of types passed as a parameter pack.
Whenever you invoke et on a specialization of S, it invokes the same method on each type used to specialize it.
I guess the example is quite clear and can serve as a good base for the final code.
If I've understood correctly the real problem, this could be a suitable design for your classes.
EDIT
I'm trying to reply to some comments to this answer that ask for more details.
A specialization of S is all the (sub)objects with which it is built.
In the example above, S<A, B> is both an A and a B.
This means that S can extend one or more classes to provide common data and can be used as in the following example to push around those data and the other subobjects:
#include<iostream>
struct I {
virtual void et() = 0;
};
struct Data {
int foo;
double bar;
};
template<typename... T>
struct S: I, Data, private T... {
S(): Data{}, T{}... {}
void et() override {
int arr[] = { (T::et(*this), 0)..., 0 };
(void)arr;
std::cout << "S" << std::endl;
}
};
struct A {
void et(Data &) {
std::cout << "A" << std::endl;
}
};
struct B {
void et(A &) {
std::cout << "B" << std::endl;
}
};
int main() {
I *ptr = new S<A,B>{};
ptr->et();
delete ptr;
}