I simplified my problem with a simple example : immagine I manage a collection of elements std::vector<Element>, each element having several members :
struct Element
{
public:
double foo;
double bar;
};
Then, I want to define an abstract class BarEvaluator, for algorithms computing the values of b from the values of a. My first idea is the following :
class BarEvaluator
{
public:
virtual void evaluate(std::vector<Element>& elements) const = 0;
};
From that, I can implement several algorithms, for example, an algorithme computing the bar values as the square of the foo values :
class SqrBarEvaluator
{
public:
virtual void evaluate(std::vector<Element>& elements) const
{
for(unsigned long int i = 0; i < elements.size(); ++i)
elements[i].bar = elements[i].foo * elements[i].foo;
}
};
This is working well. But I think it's not a really good architecture, because my algorithm is also able to modify the foo values. I don't want that.
Then I would like to be able to give my collection to the algorithm with a kind of "filter" allowing to modify only the bar variable and not the foo variable in each element. Is it possible with C++98 ? I have no idea how to do that.
Remark 1 : I don't want to do that with public or private in Element. You can immagine I also want to create algorithms FooEvaluator computing foo values from bar values, with writing access to foo and not to bar.
Remark 2 : The algorithm can require all the collection to compute each value.
Maybe you should pull the loop out of the interface.
class BarEvaluator
{
public:
virtual double evaluate(const Element& element) const = 0;
};
class SqrBarEvaluator
{
public:
virtual double evaluate(const Element& element) const
{
return element.foo * element.foo;
}
};
Then you call it like this:
std::vector<Element> elements;
...
for (std::vector<Element>::iterator it = elements.begin(); it != elements.end(); ++it) {
it->bar = barEvaluator.evaluate(*it);
}
You can use a wrapper:
class BarElementWrapper
{
public:
BarElementWrapper(Element& e) : elem(e) { }
double getFoo() { return elem.foo; }
void setBar(double b) { elem.bar = b; }
private:
Element& elem;
}
And then your algorithm receives a collection of BarElementWrapper.
Related
I've been banging my head for quite some time now trying to create an array/vector that can contain references to several types of variables, see the example:
class Validate
{
private:
some_array/vector[]; //0 would refer to x, 1 to y, and so on..
uint8_t x;
uint16_t y;
int32_t z;
public:
Validate();
void doSomething(uint8_t &member);
void doSomething(uint16_t &member);
void doSomething(int32_t &member);
}
The whole point is so that I can use this array/vector easily in a "for loop", something like this:
void Validate::doSomething(uint_8 &member)
{
//Do whatever with the variable refered to.
}
Validate::Validate()
{
for(int i = 0 ; i < 2 ; i++)
doSomething(some_array/vector[i]);
}
Perhaps somebody have an answer or possibly a better solution for me.
You have two problems. Firstly, a container cannot contain elements of different types. Secondly, you cannot store references in a container.
One way of solving the first problem is to use std::variant from C++17 (or boost::variant). To solve the second problem you could use std::reference_wrapper:
class Validate {
using Uint8Ref = std::reference_wrapper<uint8_t>;
using Uint16Ref = std::reference_wrapper<uint16_t>;
using Uint32Ref = std::reference_wrapper<int32_t>;
using MemberType = std::variant<Uint8Ref, Uint16Ref, Uint32Ref>;
std::vector<MemberType> members;
uint8_t x;
uint16_t y;
int32_t z;
public:
Validate();
void doSomething(uint8_t &member);
void doSomething(uint16_t &member);
void doSomething(int32_t &member);
};
Validate::Validate() : members({std::ref(x), std::ref(y), std::ref(z)}) {
for (auto member : members) {
std::visit([this](auto member){this->doSomething(member);}, member);
}
}
Live demo.
Alternatively, you could create a polymophic base class for a member that you can store in a container:
class MemberType {
public:
virtual ~MemberType(){}
virtual void accept(Validate& validate) = 0;
};
class Validate {
std::array<std::unique_ptr<MemberType>, 3> members;
// as before...
};
template<typename T>
class MemberTypeImpl : public MemberType {
T& member;
public:
MemberTypeImpl(T& member) : member(member){}
void accept(Validate& validate) override {
validate.doSomething(member);
}
};
template<typename T>
std::unique_ptr<MemberType> make_member_type(T& member) {
return std::make_unique<MemberTypeImpl<T>>(member);
}
Validate::Validate()
: members({make_member_type(x), make_member_type(y), make_member_type(z)}) {
for (auto& member : members) {
member->accept(*this);
}
}
I am using typeid in my code, but it seems to me that the code can be cleaner if I avoid typeid.
If we want to store the type of the class, why would we choose an object-oriented language in the first place?
But I see this pattern over and over again and I do not know how to avoid it.
So I am thinking if this code can be written cleaner with a better abstraction?
Here is the code:
class A {
public:
string type;
};
template <typename T>
class B : public A {
public:
B() {
type = typeid(T).name();
}
};
class Registry {
private:
std::vector<A *> list;
public:
void append(A * a) {
int found = 0;
for (A * el : list) {
if (a->type == el->type) {
found = 1;
break;
}
}
if (!found)
list.push_back(a);
}
int size() {
return list.size();
}
};
int main(int argc, char **argv) {
Registry reg;
A * b_int1 = new B<int>();
A * b_int2 = new B<int>();
A * b_float = new B<float>();
reg.append(b_int1);
reg.append(b_int2);
reg.append(b_float);
cout << reg.size() << endl;
return 0;
}
The output is 2. (which is the expected result)
Basically we do not want to store two object of the same type in a list.
If you don't want visitors, but you'd like a quick RTTI, I'd suggest looking into this paper: http://www.stroustrup.com/fast_dynamic_casting.pdf
The idea is:
Each class is assigned a distinct prime number for it's own type (e.g., A::my_type = 2; B::my_type = 3)
Then each class is additionally assigned the product of its type and base class values if any (e.g., A::can_cast = A::my_type; B::can_cast = B::my_type * A::can_cast; )
This solves the is_same_dynamic(), is_base_dynamic() problems elegantly: former becomes ==, latter becomes %.
To check whether or not an object belongs to a class derived from a given class, one might use the dynamic_cast<T*> and compare the result with nullptr. Unfortunately, given that we need to check this fact to the unknown type, we are forced to implement such comparison method once per each descendant of class A, but this may be simplified using #define.
Summing up, I would probably write it like this:
#define TYPE_COMPARISON \
virtual bool compare(A* rhs) \
{ \
return dynamic_cast<decltype(this)>(rhs) != nullptr; \
}
class A {
public:
TYPE_COMPARISON
};
template <typename T>
class B : public A {
public:
TYPE_COMPARISON
};
class Registry {
private:
std::vector<A *> list;
public:
void append(A * a) {
int found = 0;
for (A * el : list) {
if (a->compare(el) && el->compare(a)) {
found = 1;
break;
}
}
if (!found)
list.push_back(a);
}
int size() {
return list.size();
}
};
Also, such method allows you to define whether or not a particular descendant class should be treated as being distinct with its parent.
As an example, I have this case, in which the classes A and B perform the same expensive calculation, the function expensiveFunction. This function is "pure", in that I can guarantee that it will give the same result given the same input. The client may use both classes (or more similar classes) with the same input, and I would wish that the expensensive function is only calculated once. However, the client may also only use one class for a given input.
Code example:
class A {
public:
A(const InputData& input) {
res = expensiveFunction(input);
}
void foo(); //Use the expensive result
private:
ExpensiveResult res;
};
class B {
public:
B(const InputData& input) {
res = expensiveFunction(input); //Same function as in A
}
double bar(); //Use the expensive result
private:
ExpensiveResult res;
};
int main() {
//Get some input
//...
A a(input);
B b(input);
//Do stuff with a and b
//More input
A a2(otherInput);
//...
}
In some languages, due to referential transparency and memoization, it can safely compute it only once for a given input.
What I have thought of is using some sort factory method/class, or give a function object/functor/supension to the A and B classes that stores the result.
What are some good design ideas to solve this problem?
I own all of the code, so I can change the client or the service classes if necessary.
You can memoize just inside of your function
COutput expensive(CInput input) {
static std::map<CInput, COutput> memoized_result;
auto resit = memoized_result.find(input);
if (resit == memoized_result.end()) {
// ... do calculations
output = expensiveCalculation(input);
resit = memoized_result.insert(std::make_pair(input, output));
}
return resit->second;
}
The result of your computation is stored in the static map (memoized_result), and persisted between function calls.
If input is too expensive to use as a key in the map, you can create a separate class for handling computation result, and share it between all clients:
#include <memory>
using namespace std;
class ExpensiveResult {
public:
ExpensiveResult(int input) {
out_ = input+1;
}
int out_;
};
class BaseCompResultUser {
public:
BaseCompResultUser(const std::shared_ptr<ExpensiveResult>& res) {
res_ = res;
}
private:
std::shared_ptr<ExpensiveResult> res_;
};
class A : public BaseCompResultUser {
public:
A(const std::shared_ptr<ExpensiveResult>& r) : BaseCompResultUser(r) { }
};
class B : public BaseCompResultUser {
public:
B(const std::shared_ptr<ExpensiveResult>& r) : BaseCompResultUser(r) { }
};
int main() {
std::shared_ptr<ExpensiveResult> res(new ExpensiveResult(1));
A a(res);
B b(res);
return 0;
}
This will force sharing computation result between objects.
I think that the object-oriented way of solving it is for the expensiveFunction to be a member function of InputData (or some wrapper of InputData) and then your problem pretty much goes away. You just make ExpensiveResult a mutable cache in InputData:
class InputData {
private:
mutable std::shared_ptr<ExpensiveResult> result_;
public:
InputData() : result_(nullptr) {}
std::shared_ptr<ExpensiveResult> expensiveFunction() const {
if (!result_) {
// calculate expensive result...
result_ = std::make_shared<ExpensiveResult>();
}
return result_;
}
};
The expensive calculation is only done the first time expensiveFunction is called. You might have to add some locking if this is being called in a multi-threaded way.
If ExpensiveFunction does the same thing in A and B, it hardly seems like a true member of either. Why not a function?
int main() {
//Get some input
//...
res = expensiveFunction (input) ;
A a(res);
B b(res);
//Do stuff with a and b
//...
}
I have class called "UltrasoundTemplate". These UltrasoundTemplate objects contain an int parameter, which shows when they where defined (something like a time stamp). And I have a class called "UltrasoundTarget" which contains a vector of UltrasoundTemplate's.
I add UltrasoundTemplates to the vector with push_back(ultrasoundTemplate).
Now I want to sort the vector by the order of time stamps instead of the order I added them to the vector.
I found a lot of answers in google, which all show me the same solution, but obviously I'm still doing something wrong. Here are the code snippets I think are necessary for finding a solution:
ultrasoundTemplate.h
class UltrasoundTemplate
{
public:
UltrasoundTemplate(/*...*/);
int getVolumePos() { return volume_; }
private:
int volume_;
};
ultrasoundTarget.h
//the sort algorithm
struct MyTemplateSort {
bool operator() ( UltrasoundTemplate t1, UltrasoundTemplate t2){
int it1 = t1.getVolumePos();
int it2 = t2.getVolumePos();
if (it1 < it2)
return true;
return false;
}
};
class UltrasoundTarget
{
public:
UltrasoundTarget(/*...*/);
vector<UltrasoundTemplate> getTemplates() { return USTemplateVector_; }
private:
vector<UltrasoundTemplate> USTemplateVector_;
};
FMainWindow.cpp
void FMainWindow::match_slot()
{
int i;
//here I get the name of the target I'm looking for
QTreeWidgetItem *item = targetInfoWidget_->treeWidget->currentItem();
int index = targetInfoWidget_->treeWidget->indexOfTopLevelItem(item);
QString itemToAppendName = item->text(0);
for(i = 0; i < USTargetVector.size(); i++){
if(USTargetVector.at(i).getName() == itemToAppendName) {
//here I try to sort
MyTemplateSort tmpltSrt;
std::sort(USTargetVector.at(i).getTemplates().begin(),
USTargetVector.at(i).getTemplates().end(), tmpltSrt);
break;
}
}
As an example: I define Template1 in Volume(0), Template2 in Volume(70) and Template3 in Volume(40). The order now is (Template1, Template2, Template3) but I want it to be (Template1, Template3, Template2). But this code is not doing it.
If there's Information missing, just tell me and I'll provide more code.
Thanks alot.
Your getTemplates() method returns by value, making a mess here:
std::sort(USTargetVector.at(i).getTemplates().begin(),
USTargetVector.at(i).getTemplates().end(), tmpltSrt);
You are sorting an incompatible iterator range. You can fix that particular problem by returning a reference:
vector<UltrasoundTemplate>& getTemplates() { return USTemplateVector_; }
It is common practice to add a const overload to such a method:
const vector<UltrasoundTemplate>& getTemplates() const { return USTemplateVector_; }
You can also modify your comparison functor to avoid unnecessary copies (and for general readability and const correctness):
struct MyTemplateSort {
bool operator() const ( const UltrasoundTemplate& t1, const UltrasoundTemplate& t2)
{
return t1.getVolumePos() < t2.getVolumePos();
}
};
This will require that you make getVolumePos() a const method, which it should be anyway:
class UltrasoundTemplate
{
public:
...
int getVolumePos() const { return volume_; }
...
};
Note that is is not generally good practice to provide references to the private data of a class. If possible, you should find a way to remove that from the UltraSoundTarget interface. You could, for instance, expose a pair of iterators, and/or give the class a sort method.
juanchopanza answer is correct, the problem is the way you are returning the vector from UltrasoundTarget. Just to touch another topic, maybe it would be nice to change a little the designing of your implementation. As UltrasoundTarget is a container of Ultrasound's, it makes sense to implement the sort as a method of this class, this way you have direct access to USTemplateVector_ and will save unecessary copies. Something like:
class UltrasoundTarget
{
public:
UltrasoundTarget(/*...*/);
vector<UltrasoundTemplate> getTemplates() { return USTemplateVector_; }
void sort();
private:
vector<UltrasoundTemplate> USTemplateVector_;
};
void UltrasoundTarget::sort()
{
std::sort(USTemplateVector_.begin(), USTemplateVector_.end(), tmpltSrt);
}
void FMainWindow::match_slot()
{
int i;
//here I get the name of the target I'm looking for
QTreeWidgetItem *item = targetInfoWidget_->treeWidget->currentItem();
int index = targetInfoWidget_->treeWidget->indexOfTopLevelItem(item);
QString itemToAppendName = item->text(0);
for(i = 0; i < USTargetVector.size(); i++){
if(USTargetVector.at(i).getName() == itemToAppendName)
{
//here I try to sort
MyTemplateSort tmpltSrt;
USTargetVector.at(i).sort();
break;
}
}
I have this class:
class CComputer {
public:
// constructor
CComputer(string name) {
this->name = name;
};
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component const & component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
string name;
string address;
list<Component> listOfComponents;
};
and then these classes:
// ancestor for other classes...It's really dummy yet, but I dunno what to add there
class Component {
public:
Component() {};
~Component() {};
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) {
this->mem = mem;
};
int mem;
};
Now I feed my CComputer class with some values:
CComputer c("test.com");
c . AddAddress("123.45.678.910") .
AddComponent(CCPU(8, 2400)) .
AddComponent(CCPU(8, 1200)).
AddComponent(CMemory(2000)).
AddComponent(CMemory(2000)));
And now I would like to print it out with all the info I've put in there (CCPU & CMemory details including)
but how to implement it, to be able to iterate through CComputer::listOfComponents and don't care if I acctually access CCPU or CMemory ? I can add it to that list, but I have really no idea, how to make it, to be able to access the variables of those components.
So the output should look like:
##### STARTING #####
CComputer:
name:test.com
address:123.45.678.910
CCPU:
cores:8,freq:2400
CCPU:
cores:8, freq:1200
CMemory:
mem:2000
CMemory:
mem:2000
###### FINISHED! #####
As others have mentioned, you need to implement a virtual function (e.g. virtual std::string ToString() const = 0;) in the base class that is inherited and overridden by each child class.
However, that isn’t enough. Your code exhibits slicing which happens when you copy your child class instances into the list: the list contains objects of type Component, not of the relevant child class.
What you need to do is store polymorphic instances. Values themselves are never polymorphic, you need to use (smart) pointers or references for this. References are out, however, since you cannot store them in a standard container (such as std::list). Using raw pointers is considered bad style nowadays, but judging from the naming conventions of your classes you don’t learn modern C++ in your class (sorry!).
Therefore, raw pointers is probably the way to go. Change your code accordingly:
Store a list of pointers:
list<Component*> listOfComponents;
Make the argument type of AddComponent a pointer instead of const&.
Call the function by passing a newed object, e.g.:
AddComponent(new CCPU(8, 2400))
Now your code leaks memory left, right and center. You need to implement a destructor to free the memory:
~CComputer() {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i)
delete *i;
}
But now your code violates the Rule of Three (read this article! It’s important, and it may be the most useful thing about C++ you’re going to learn in this programming class) and consequently you also need to implement the copy constructor and copy assignment operator. However, we can’t. Sorry. In order to implement copying for your class, you would have to implement another virtual function in your Component class, namely one that clones an object (virtual Component* Clone() const = 0;). Only then can we proceed.
Here’s a sample implementation in CCPU:
Component* Clone() const {
return new CCPU(cores, freq);
}
… this needs to be done in all classes deriving from Component, otherwise we cannot correctly copy an object of a type that derives from Component and is hidden behind a pointer.
And now we can implement copying in the CComputer class:
CComputer(CComputer const& other)
: name(name)
, address(addess) {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
}
CComputer& operator =(CComputer const& other) {
if (this == &other)
return *this;
name = other.name;
address = other.address;
listOfComponents.clear();
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
return *this;
}
This code is brittle, not thread-safe and error-prone and no competent C++ programmer would ever write this1. Real code would for instance use smart pointers instead – but as mentioned before I’m pretty sure that this would be beyond the scope of the class.
1 What does this make me now, I wonder?
Just add a virtual method to Class Component called e.g. toString(), which returns a string describing the component. Then you can iterate through all components and call toString() without worrying about exactly what each component is. If you do that, then for each computer you would be able to print out the values of all the components.
However, as pointed out in one of the comments, the example output you give in the question outputs the CCPU for all computers, then all the memory for all computers. To order the output like that, you'll need to add another virtual method to Component called e.g. getType() which returns an enum or integer that represents the type of the information. You can then have two for-next loops, one nested inside the other, where the outer loop iterates through all the types and the inner loop iterating through all the computers calling the toString() on all components which match the type specified in the outer for loop.
Here's something that implements this idea.
#include <iostream>
#include <string>
#include <list>
using namespace std;
int const TYPE_CCPU = 1;
int const TYPE_MEMORY = 2;
class Component {
public:
virtual int GetType() { return -1; }
virtual std::string ToString() const {
return "OOPS! Default `ToString` called";
}
};
class CComputer {
public:
typedef std::list<Component*>::iterator iter_t;
// constructor
CComputer(string name) {
this->name = name;
};
~CComputer() {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
delete *i;
}
}
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component *component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
void PrintType(int type) {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
if ((*i)->GetType() == type)
std::cout << (*i)->ToString() << '\n';
}
}
string name;
string address;
list<Component*> listOfComponents;
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int GetType() { return TYPE_CCPU; }
std::string ToString() const {
return "CCPU::ToString()";
}
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) { this->mem = mem; };
int GetType() { return TYPE_MEMORY; }
std::string ToString() const {
return "CMemory::ToString()";
}
int mem;
};
typedef std::list<CComputer*>::iterator iter_c;
int main() {
list<CComputer*> computerlist;
CComputer *c1 = new CComputer("test.com"), *c2 = new CComputer("test2.com");
c1->AddAddress("123.45.678.910").
AddComponent(new CCPU(8, 1200)).
AddComponent(new CMemory(2000));
computerlist.push_back(c1);
c2->AddAddress("987.65.432.10").
AddComponent(new CCPU(8, 2400)).
AddComponent(new CMemory(4000));
computerlist.push_back(c2);
for(int t=TYPE_CCPU; t<=TYPE_MEMORY; t++)
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
(*i)->PrintType(t);
}
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
delete (*i);
}
}
Implement ToString() in each of your classes. In .NET this is a standard even the "object" type implements.