I have a vector:
std::vector<uint16_t> free_ids;
I need for it operator== of my class GameObject. When an object is created, it will receive free id from vector, thus it will be "moved" from it to object. It will get values simply like this:
void init(void)
{
for(uint64_t i=0; i<30; ++i)
free_ids.push_back(i);
}
So I have class that uses that succesfully.
class GameObject
{
public:
static std::vector<GameObject*> created_objects; // all objects created ever
static constexpr auto& CO = created_objects;
GameObject()
{
id = free_ids.front(); // get id from vector
free_ids.erase(free_ids.begin()); // delete it from vector
CO.push_back(this); // add address to all object created ever
}
GameObject(const GameObject& other)
{
// copy attributes I didn't include in this code
id = free_ids.front();
free_ids.erase(free_ids.begin());
CO.push_back(this);
}
~GameObject()
{
free_ids.push_back(id); // return id to vector
CO.erase(std::remove(CO.begin(), CO.end(), this), CO.end());
// remove object by address
}
bool operator==(const GameObject& other)
{
return id==other.id; // check if id is the same (if it's the same object)
}
const uint64_t& get_id(void) const
{
return id;
}
private:
uint64_t id;
};
std::vector<GameObject*> GameObject::created_objects;
I'd love to have global constant of type GameObject, but it will cause segmentation fault, because init() was never called before main() call
//const GameObject Progenitor; //segmentation fault, free_ids not initialized yet
And a sample program:
int main()
{
srand(time(NULL));
init();
const GameObject Progenitor; // It's temporary replacement for my global
std::vector<GameObject> clone_army;
clone_army.reserve(20); // GameObjects can't be reallocated bacause
// their addresses are stored in class static vector
auto& CA = clone_army;
for(uint64_t i=0; i<20; ++i)
CA.push_back(Progenitor);
std::cout << "Storage used. Unoccupied ids: " << std::endl;
for(auto& x : free_ids)
std::cout << x << std::endl;
auto& victim = clone_army[rand()%20]; // it's much more compilated
std::cout << "\nOne will die. Victim is... clone no. " << victim.get_id() << std::endl;
CA.erase(std::remove(CA.begin(), CA.end(), victim), CA.end());
// need to remove victim by value, operator== involved
std::cout << "\nProgenitor id: ";
for(auto& x : GameObject::CO)
std::cout << x->get_id() << std::endl;
}
Responsible headers:
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
My question is, how to initialize std::vector<uint16_t> free_ids; - which can't be const, before any object of GameObject class is ever created?
(There will be many progenitors of different inherited classes, template-like objects that I will use (already am but want to rearrange code) to create real-time clones)
While it is easy to create a static object which will initialize the vector in it's constructor, you can never guarantee that this static object will be initialized before all other static objects in different translation units.
Instead, what you might do is to employ a singleton-type thing. Within this singleton, you can expose get_id and release_id functions. Judging by the code provided, I do not think you need me to sketch out this singleton for you, but if you do, feel free to request.
To be honest, you could just do this.
class GameObject
{
private:
using InstanceId = unsigned long long;
static InstanceId _OBJECT_ID = 0;
protected:
const InstanceId mId;
public:
GameObject()
: mId(_OBJECT_ID++)
{}
};
Of course you could get conflicts if your game spawns more than 18446744073709551615 objects during a run.
Easy option:
You could define a helper class, that make sure that your vecotr is initialized only once:
class GameObjectHelper {
static bool done;
public:
GameObjectHelper() {
if (!done) {
init();
done = false;
}
}
};
bool GameObjectHelper::done = false;
Then you can make sure that the constructor of this object is called before the constructor of GameObject by taking benefit of the fact that a base class is constructed before its derived:
class GameObject : private GameObjectHelper {
...
} ;
Important edit: If your GameObjects are multithreaded, done will be initialized to false only once in a thread safe fashion. Unfortunately, several threads could enter the if statement and lead to a race condition. I didn't address this topic in first place, because your remaining code doesn't show evidence of multithreading: your access to the global vector is not thread safe and might lead to adverse race conditions. If you really need multithreading, and if you can't apply the second option, then you should use atomic done with compare_exchange_strong() to test its value.
Cleaner variant:
If you want to avoid your vector being global, you could as well define a helper class such as:
class GameObjectHelper {
vector<uint16_t> free_ids;
public:
GameObjectHelper() {
for(uint64_t i=0; i<30; ++i)
free_ids.push_back(i);
}
}
};
and create a static member obect in the GameObject class:
class GameObject {
protected:
static GameObjectHelper goh;
...
} ;
Of course this works if and only if your vector is solely used by GameObject and its derivates.
But this one is threadsafe, as there the static object is guaranteed to be initialized only once.
Related
I'm working on a call center call queue simulation model. I've created a vector of caller objects and assigned them exponential distributed random inter-arrival times, then assigned calculated arrival times in the call center class. I would like to then copy each caller object into a vector priority queue, however I keep receiving this error when i push a caller vector object into the queue:
Error C2280 'Caller::Caller(const Caller &)': attempting to reference a deleted function
I've been trying to fix it for a while now and cannot seem to figure out what is causing the issue or how to fix it. I'm trying to push an already created object, so I'm not sure why I would get a reference to a deleted function. Any help would be appreciated.
My Caller.h file
#pragma once
#include <random>
#include <time.h>
using namespace std;
class Caller
{
private:
bool isPaid;
int priority;
double arrivalTime;
double iarrivalTime;
default_random_engine e1;
random_device rd1;
public:
Caller();
Caller(bool p);
void setPriority();
int getPriority();
void generateInterArrivalTime();
double getInterArrivalTime();
void setArrivalTime(double t);
double getArrivalTime();
};
My Caller.Cpp file
#include "Caller.h"
Caller::Caller() : isPaid(false), priority(0), iarrivalTime(0), arrivalTime(0)
{
}
Caller::Caller(bool p): isPaid(false), priority(0), iarrivalTime(0)
{
isPaid = p;
}
void Caller::setPriority()
{
if (isPaid == true)
{
priority = 1;
}
else(priority = 0);
}
int Caller::getPriority()
{
return priority;
}
void Caller::generateInterArrivalTime()
{
e1.seed(rd1());
exponential_distribution<>callNums(25);
iarrivalTime = callNums(e1);
}
double Caller::getInterArrivalTime()
{
return iarrivalTime;
}
void Caller::setArrivalTime(double t)
{
arrivalTime = t;
}
double Caller::getArrivalTime()
{
return arrivalTime;
}
My CallCenter.h file
class CallCenter
{
private:
vector<Caller> callers;
priority_queue<Caller, vector<Caller>, CompareFunction > callQ;
public:
CallCenter();
void queueCalls();
void assignArrivalTime();
My CallCenter.Cpp file
CallCenter::CallCenter(): callers(10)
{
}
void CallCenter::assignArrivalTime()
{
for (int i = 0; i < callers.size(); i++)
{
callers[i].generateInterArrivalTime();
if (i==0)
{
callers[i].setArrivalTime(callers[i].getInterArrivalTime());
}
else {callers[i].setArrivalTime(callers[i - 1].getArrivalTime() + callers[i].getInterArrivalTime());}
cout << callers[i].getInterArrivalTime() << "\t" << callers[i].getArrivalTime() << endl;
}
}
void CallCenter::queueCalls()
{
for (int i = 0; i < callers.size(); i++)
{
callQ.push(callers[i]);
}
}
My CompareFunction.h file
#pragma once
#include "Caller.h"
class CompareFunction
{
public: bool operator()(Caller& lowp, Caller& highp)
{
return lowp.getArrivalTime() > highp.getArrivalTime();
}
};
random_device rd1;
Your class has a std::random_device as a class member.
std::random_device's copy constructor is deleted:
The copy constructor is deleted: std::random_device is not copyable
nor movable.
This makes this class, which contains this class member, also have a deleted copy constructor.
After all, if a class member cannot be copied, by default, then the class itself can't be copied by default either.
priority_queue<Caller, vector<Caller>, CompareFunction > callQ;
Your priority queue is based on a std::vector.
callQ.push(callers[i]);
std::vectors cannot be used with non-copyable/movable classes. You can only use std::vector with classes that can be copied or moved.
You will have to change your class design, in some form or fashion. The simplest change would be a priority queue of std::unique_ptrs or std::shared_ptrs to your Callers, which you will need to construct in dynamic scope (you will also have to provide a custom comparator class for std::priority_queue, so it knows how to prioritize the smart pointers correctly, this is a little bit of extra work but it's not too complicated once you have a complete grasp on all the moving pieces).
I come from C/C# language and now I'm trying to learn about C++ and his standards functions.
Now, I'm creating a class called IMonsterDead. I will have a std::vector<IMonsterDead*> with N monsters.
Example:
class IMonsterDead {
public:
IMonsterDead(int Id)
{
this->_Id = Id;
}
virtual void OnDead() = 0;
int Id() const {
return _Id;
}
private:
int _Id;
};
One class which implements that class:
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
virtual void OnDead()
{
std::cout << "MonsterTesd died" << std::endl;
}
};
Ok, if I access directly everything works fine. But I'm trying to use std::find.
Full program test:
int main()
{
std::vector<IMonsterDead*> monsters;
for (int i = 0; i < 1000; i++)
{
monsters.emplace_back(new MonsterTest(1000 + i));
}
int id = 1033;
std::vector<IMonsterDead*>::iterator result = std::find(monsters.begin(), monsters.end(), [id]( IMonsterDead const* l) {
return l->Id() == id;
});
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
// Here I want to access OnDead function from result
}
return 0;
}
So I need to access OnDead function from result but I can't. Intellisense doesn't show anything for me. The result exists.
How can I access that function? Have another better way to do that?
You need to use std::find_if() instead of std::find(). std::find() is for finding an element with a specific value, so you have to pass it the actual value to find, not a user_defined predicate. std::find_if() is for finding an element based on a predicate.
Either way, if a match is found, dereferencing the returned iterator will give you a IMonsterDead* pointer (more accurately, it will give you a IMonsterDead*& reference-to-pointer). You need to then dereference that pointer in order to access any members, like OnDead().
You are also leaking memory. You are not delete'ing the objects you new. And when dealing with polymorphic types that get deleted via a pointer to a base class, the base class needs a virtual destructor to ensure all derived destructors get called properly.
With that said, you are clearly using C++11 or later (by the fact that you are using vector::emplace_back()), so you should use C++11 features to help you manage your code better:
You should use std::unique_ptr to wrap your monster objects so you don't need to delete them manually.
You should always use the override keyword when overriding a virtual method, to ensure you override it properly. The compiler can catch more syntax errors when using override than without it.
You should use auto whenever you declare a variable that the compiler can deduce its type for you. Especially useful when dealing with templated code.
Try something more like this:
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
class IMonsterDead {
public:
IMonsterDead(int Id)
: m_Id(Id)
{
}
virtual ~IMonsterDead() {}
virtual void OnDead() = 0;
int Id() const {
return m_Id;
}
private:
int m_Id;
};
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
void OnDead() override
{
std::cout << "MonsterTest died" << std::endl;
}
};
int main()
{
std::vector<std::unique_ptr<IMonsterDead>> monsters;
for (int i = 0; i < 1000; i++)
{
// using emplace_back() with a raw pointer risks leaking memory
// if the emplacement fails, so push a fully-constructed
// std::unique_ptr instead, to maintain ownership at all times...
monsters.push_back(std::unique_ptr<IMonsterDead>(new MonsterTest(1000 + i)));
// or:
// std::unique_ptr<IMonsterDead> monster(new MonsterTest(1000 + i));
// monsters.push_back(std::move(monster));
// or, if you are using C++14 or later:
// monsters.push_back(std::make_unique<MonsterTest>(1000 + i));
}
int id = 1033;
auto result = std::find_if(monsters.begin(), monsters.end(),
[id](decltype(monsters)::value_type &l) // or: (decltype(*monsters.begin()) l)
{
return (l->Id() == id);
}
// or, if you are using C++14 or later:
// [id](auto &l) { return (l->Id() == id); }
);
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
auto &monster = *result; // monster is 'std::unique_ptr<IMonsterDead>&'
monster->OnDead();
}
return 0;
}
Iterators are an interesting abstraction, in this case to be reduced to pointers.
Either you receive the pointer to the element or you get an invalid end.
You can use it as a pointer: (*result)->func();
You can also use it to create a new variable:
IMonsterDead &m = **result;
m.func();
This should give the same assembly, both possible.
Consider the following code snippet:
struct Base { virtual void func() { } };
struct Derived1 : Base { void func() override { print("1"); } };
struct Derived2 : Base { void func() override { print("2"); } };
class Manager {
std::vector<std::unique_ptr<Base>> items;
public:
template<class T> void add() { items.emplace_back(new T); }
void funcAll() { for(auto& i : items) i->func(); }
};
int main() {
Manager m;
m.add<Derived1>();
m.add<Derived2>();
m.funcAll(); // prints "1" and "2"
};
I'm using virtual dispatch in order to call the correct override method from a std::vector of polymorphic objects.
However, I know what type the polymorphic objects are, since I specify that in Manager::add<T>.
My idea was to avoid a virtual call by taking the address of the member function T::func() and directly storing it somewhere. However that's impossible, since I would need to store it as void* and cast it back in Manager::funcAll(), but I do not have type information at that moment.
My question is: it seems that in this situation I have more information than usual for polymorphism (the user specifies the derived type T in Manager::add<T>) - is there any way I can use this type information to prevent a seemingly unneeded virtual call? (An user should be able to create its own classes that derive from Base in its code, however.)
However, I know what type the polymorphic objects are, since I specify that in Manager::add<T>.
No you don't. Within add you know the type of the object that's being added; but you can add objects of different types, as you do in your example. There's no way for funcAll to statically determine the types of the elements unless you parametrise Manager to only handle one type.
If you did know the type, then you could call the function non-virtually:
i->T::func();
But, to reiterate, you can't determine the type statically here.
If I understand well, you want your add method, which is getting the class of the object, to store the right function in your vector depending on that object class.
Your vector just contains functions, no more information about the objects.
You kind of want to "solve" the virtual call before it is invoked.
This is maybe interesting in the following case: the function is then called a lot of times, because you don't have the overhead of solving the virtual each time.
So you may want to use a similar process than what "virtual" does, using a "virtual table".
The implementation of virtual is done at low level, so pretty fast compared to whatever you will come up with, so again, the functions should be invoked a LOT of times before it gets interesting.
One trick that can sometimes help in this kind of situation is to sort the vector by type (you should be able to use the knowledge of the type available in the add() function to enforce this) if the order of elements doesn't otherwise matter. If you are mostly going to be iterating over the vector in order calling a virtual function this will help the CPU's branch predictor predict the target of the call. Alternatively you can maintain separate vectors for each type in your manager and iterate over them in turn which has a similar effect.
Your compiler's optimizer can also help you with this kind of code, particularly if it supports Profile Guided Optimization (POGO). Compilers can de-virtualize calls in certain situations, or with POGO can do things in the generated assembly to help the CPU's branch predictor, like test for the most common types and perform a direct call for those with a fallback to an indirect call for the less common types.
Here's the results of a test program that illustrates the performance benefits of sorting by type, Manager is your version, Manager2 maintains a hash table of vectors indexed by typeid:
Derived1::count = 50043000, Derived2::count = 49957000
class Manager::funcAll took 714ms
Derived1::count = 50043000, Derived2::count = 49957000
class Manager2::funcAll took 274ms
Derived1::count = 50043000, Derived2::count = 49957000
class Manager2::funcAll took 273ms
Derived1::count = 50043000, Derived2::count = 49957000
class Manager::funcAll took 714ms
Test code:
#include <iostream>
#include <vector>
#include <memory>
#include <random>
#include <unordered_map>
#include <typeindex>
#include <chrono>
using namespace std;
using namespace std::chrono;
static const int instanceCount = 100000;
static const int funcAllIterations = 1000;
static const int numTypes = 2;
struct Base { virtual void func() = 0; };
struct Derived1 : Base { static int count; void func() override { ++count; } };
int Derived1::count = 0;
struct Derived2 : Base { static int count; void func() override { ++count; } };
int Derived2::count = 0;
class Manager {
vector<unique_ptr<Base>> items;
public:
template<class T> void add() { items.emplace_back(new T); }
void funcAll() { for (auto& i : items) i->func(); }
};
class Manager2 {
unordered_map<type_index, vector<unique_ptr<Base>>> items;
public:
template<class T> void add() { items[type_index(typeid(T))].push_back(make_unique<T>()); }
void funcAll() {
for (const auto& type : items) {
for (auto& i : type.second) {
i->func();
}
}
}
};
template<typename Man>
void Test() {
mt19937 engine;
uniform_int_distribution<int> d(0, numTypes - 1);
Derived1::count = 0;
Derived2::count = 0;
Man man;
for (auto i = 0; i < instanceCount; ++i) {
switch (d(engine)) {
case 0: man.add<Derived1>(); break;
case 1: man.add<Derived2>(); break;
}
}
auto startTime = high_resolution_clock::now();
for (auto i = 0; i < funcAllIterations; ++i) {
man.funcAll();
}
auto endTime = high_resolution_clock::now();
cout << "Derived1::count = " << Derived1::count << ", Derived2::count = " << Derived2::count << "\n"
<< typeid(Man).name() << "::funcAll took " << duration_cast<milliseconds>(endTime - startTime).count() << "ms" << endl;
}
int main() {
Test<Manager>();
Test<Manager2>();
Test<Manager2>();
Test<Manager>();
}
I have a constant value that never changes during run-time, but is impossible to know until run-time.
Is there a way to declare a constant (either as a member of a class or not) without defining it and also assign a computed value once (and only once) it is determined; or am I going to have to resort to a non-const declaration and use coding S & Ps (ALL_CAPS variables names, static declaration if in a class, etc.) to try and keep it from changing?
CLARIFICATION:
Though these are good answers, the real-world situation I have is more complicated:
The program has a main loop that continually runs between processing and rendering; the user can set required options and once they are set they will never change until the program is restart. An "Initialize" function is set up for anything that can be determined before the main loop, but values that are dependent on user interaction must be performed in the middle of the loop during the processing phase. (At the moment, persistent data storage techniques come to mind...)
Something like this?
const int x = calcConstant();
If it's a class member, then use the constructor initialisation list, as in Yuushi's answer.
You can define it in a struct or class and utilize an initialisation list:
#include <iostream>
struct has_const_member
{
const int x;
has_const_member(int x_)
: x(x_)
{ }
};
int main()
{
int foo = 0;
std::cin >> foo;
has_const_member h(foo);
std::cout << h.x << "\n";
return 0;
}
As a static or function-local variable:
const int x = calcConstant();
As a class member:
struct ConstContainer {
ConstContainer(int x) : x(x) {}
const int x;
};
Yes, you can make a private static singleton field with an initialization method and a gettor method. Here's an example of how to do it:
// In foo.h
class Foo
{
public:
// Caller must ensure that initializeGlobalValue
// was already called.
static int getGlobalValue() {
if (!initialized) {
... handle the error ...
}
return global_value;
}
static void initializeGlobalValue(...)
private:
static bool initialized;
static int global_value;
};
// In foo.cpp
bool Foo::initialized = false;
int Foo::global_value;
void Foo::initializeGlobalValue(...) {
if (initialized) {
...handle the error...
}
global_value = ...;
initialized = true;
}
I am updating an old piece of C++ code and am stuck on a design issue and need advice on the best course of action. The code handles geometric data. Currently, the code defines many global constants to handle element types:
#define TETRAHEDRON 0
#define HEXAHEDRON 1
Each constant has information associated with it that remains constant and which is currently handled by a class, in our case Topology.
int Topology::nodesPerElement(int topType)
{
switch(topType) {
case TETRAHEDRON:
return 4;
break;
case HEXAHEDRON:
return 8;
break;
}
}
The Topology class has many of these functions that simply switch on the global constant to figure out associated information. There are a lot of element types and many bugs are introduced by switch statements that don't consider all element types. If an element type is added all of these methods need to be fixed. I need a better way of doing this that keeps the associated information with the type.
Enumerations are an improvement over this design, but it doesn't solve the problem of associating data with the enumeration.
For simplicity, I would like to avoid needing to instantiate classes for each type, as each will contain only static data that doesn't change.
What I really need is a "static class" that holds this information and performs like the pseudocode below:
class Tetrahedron : public TopType {
static const int nodesPerElement = 4;
static const std::string name = "Tet";
etc...
}
Each method in Topology becomes trivial:
int Topology::nodesPerElement(TopType topType)
{
return topType.nodesPerElement;
}
Is there a way to do this in C++? I've thought about just getting rid of the enumerations and having separate child Topology classes for each TopologyType, but the feedback I get from others is that it's too complicated of a solution. I hope that my question is clear enough.
Create a base class that contains all of the properties that your objects should support, and a private constructor to set those properties. You don't need derived classes, then: you can use static public objects to create the objects that you want with the desired properties.
class TopologyObject
{
private:
int numberVertices;
int numberFaces;
// etc.
public:
int getVertices() { return numberVertices; };
int getFaces() { return numberFaces; };
protected:
TopologyObject(int vertices, int faces) :
numberVertices(vertices),
numberFaces(faces)
{};
public:
static TopologyObject Tetrahedron = new TopologyObject(4, 4);
// etc.
}
You can access the Tetrahedron with all of its properties via TopologyObject::Tetrahedron.
If you decide that you need more complex variable behavior based on the type of object, then you really do need derived classes and virtual methods for the overrideable behavior.
Unless your Topology types have different runtime behaviors (like drawing themselves), then I agree with your peers that sub-classing is overkill. Reporting static properties like nodesPerElement and name is hardly a runtime behavior.
Unless you are not telling us the whole story about Topology, it seems that what you need is a simple property map. Use std::map to associate a topology type code with a structure of topology properties. This refactoring resembles Replace Subclass with Fields.
Here's some code that may serve as inspiration:
#include <cassert>
#include <iostream>
#include <map>
#include <string>
struct Topology
{
enum Code {tetrahedron, hexahedron};
int nodesPerElement;
std::string name;
};
namespace // Anonymous namespace
{
// Lookup table associating topology code with properties
const struct {Topology::Code code; Topology topo;} topoTable_[] =
{
{Topology::tetrahedron, {4, "Tetrahedron"}},
{Topology::hexahedron, {6, "Hexahedron"}}
};
};
class TopologyMap // Singleton
{
public:
static TopologyMap lookup(Topology::Code code)
{
return Topology(instance().doLookup(code));
}
private:
typedef std::map<Topology::Code, Topology> Map;
Map map_;
TopologyMap()
{
// Initialize map with constant property table
size_t tableSize = sizeof(topoTable_) / sizeof(topoTable_[0]);
for (size_t row=0; row<tableSize; ++row)
{
map_[topoTable_[row].code] = topoTable_[row].topo;
}
}
static TopologyMap& instance()
{
static TopologyMap instance;
return instance;
}
const Topology& doLookup(Topology::Code code) const
{
Map::const_iterator match = map_.find(code);
assert(match != map_.end());
return match->second;
}
};
class Shape
{
public:
Shape(Topology::Code topoCode)
: topo_(TopologyMap::lookup(topoCode)) {}
const Topology& topology() const {return topo_;}
// etc...
private:
Topology topo_;
};
int main()
{
Shape shape1(Topology::tetrahedron);
Shape shape2(Topology::hexahedron);
std::cout << "shape1 is a " << shape1.topology().name << " with " <<
shape1.topology().nodesPerElement << " nodes per element.\n";
std::cout << "shape2 is a " << shape2.topology().name << " with " <<
shape2.topology().nodesPerElement << " nodes per element.\n";
};
Output:
shape1 is a Tetrahedron with 4 nodes per element.
shape2 is a Hexahedron with 6 nodes per element.
If the topology code is zero-based and continuous, then you may use simple array indexing instead of a map. However, array indexing will be more error-prone if someone messes around with the topology code enum. Here is the same example that uses array indexing:
#include <cassert>
#include <iostream>
#include <map>
#include <string>
struct Topology
{
enum Code {tetrahedron, hexahedron, CODE_COUNT};
int nodesPerElement;
std::string name;
};
namespace // Anonymous namespace
{
// Lookup table associating topology code with properties
const Topology topoTable_[] =
{
{4, "Tetrahedron"},
{6, "Hexahedron"}
};
};
class TopologyMap // Singleton
{
public:
static Topology lookup(Topology::Code code)
{
assert(code < Topology::CODE_COUNT);
return topoTable_[code];
}
private:
TopologyMap() {} // Non-instantiable
};
class Shape
{
public:
Shape(Topology::Code topoCode)
: topo_(TopologyMap::lookup(topoCode)) {}
const Topology& topology() const {return topo_;}
// etc...
private:
Topology topo_;
};
int main()
{
Shape shape1(Topology::tetrahedron);
Shape shape2(Topology::hexahedron);
std::cout << "shape1 is a " << shape1.topology().name << " with " <<
shape1.topology().nodesPerElement << " nodes per element.\n";
std::cout << "shape2 is a " << shape2.topology().name << " with " <<
shape2.topology().nodesPerElement << " nodes per element.\n";
};
Note that because the details of storing and retrieving Topology was encapsulated in TopologyMap, I didn't have to rewrite any code in Shape and main.
You can have classes with nothing but static member variables. And that's a nice way to encapsulate attribute data.
If you'd rather not do that, traits might get you what you want.
I'm not sure who advised you to avoid derived classes for each Toplogy type. To my eye, this problem is screaming for derived classes.
Unless you would need a very large number of such classes.
Personally I think the best way to store this information would be to create a general Shape class. Then, instead of coding all those static variables put them in a file/database and load your shape information from the data store when you start your program.
Couldn't you use a record to do this if your goal is to avoid class instantiation?
Really though, you should class the poop out of this.
If topType is contiguous and starting a 0, you could just maintain an array of structs and index into that, instead of trying to have classes and subclasses. This way the only code change you would need is to
add the struct: Easy
add an array of structs: Easy
change each method to index into array and return proper field of struct: Tedious, but you have to do this anyway.
It your TopologyType can just be modelled as an instance of a struct (i.e no methods on it etc), Classes + Derived classes is overkill, IMO.
Since (apparently) all the relevant data is available at compile time, one possibility would be to use an enumeration along with templates and specialization to do the job:
enum { tetrahedron, hexahedron };
template <int type>
struct nodes_per_element { int operator()() const {
throw std::invalid_argument("Attempt to use unknown shape");
};
template <>
struct nodes_per_element<tetrahedron> { int operator()() const { return 4; } };
template <>
struct nodes_per_element<hexahedron> { int operator()() const { return 8; } };
You'd use this like: int x = nodes_per_element<hexahedron>()(); If you try to use it for a value for which there's no specialization, that will invoke the un-specialized template, which will throw an exception, halting the program and (normally) displaying a message saying you attempted to use an unknown shape. Of course, you can customize how that's displayed (if at all).
This should quickly show where you have problems due to values that haven't been defined.
The other obvious possibility would be to just define a struct for each shape you're going to use, and create an array of those structs, using the name of the shape as an index into the data, and the name of the specific data you want will be the member of the struct. For just the nodes per element you've given, that would look like:
struct shape_data {
int nodes_per_element;
std::string name;
};
shape_data data[] = {
{4, "Tetrahedron"},
{8, "Hexahedron" }
};
Retrieving data would be something like:
shape_data &s = data[hexahedron];
std::cout << "A " << s.name << " has " << s.nodes_per_element << "nodes per element.\n";
Having look at the previous answers, I've decided to add my own.
To me there are 2 things that I would require of such a design:
the ability to define a new item without recompiling the whole program
the ability to look up an item based on a property (like the number of faces)
This can be quite easy to do, so here is my little bit of code:
class Solid
{
typedef std::vector<Solid> solids_type;
public:
Solid(std::string name, size_t faces, size_t nodes):
mName(name), mFaces(faces), mNodes(nodes)
{
}
///
/// Properties
///
const std::string& getName() const { return mName; }
size_t getFaces() const { return mFaces; }
size_t getNodes() const { return mNodes; }
///
/// Collection Handling
///
static bool Add(Solid solid); // only add if it's not already there.
///
/// struct Predicate: std::unary_function<Solid,bool>
///
template <class Predicate>
static const Solid* Get(Predicate pred)
{
solids_type::const_iterator it =
std::find_if(Solids().begin(), Solids().end(), pred);
return it == Solids().end()) ? 0 : &(*it);
} // Get
///
/// Some Predicates
///
class ByName: std::unary_function<Solid,bool>
{
public:
ByName(std::string name): mName(name) {}
bool operator()(const Solid& s) const { return s.getName() == mName; }
private:
std::string mName;
};
class ByFaces; /// ...
class ByNodes; /// ...
private:
/// Properties
std::string mName;
size_t mFaces;
size_t mNodes;
/// Collection
static solids_type& Solids()
{
static solids_type MSolids;
return MSolids;
}
}; // class Solid
And thus, now we can have:
// in tetrahedron.cpp
namespace
{
bool gTetrahedron = Solid::Add(Solid("Tetrahedron", 4, 4));
}
// in main.cpp
int main(int argc, char* argv[])
{
const Solid* myTetra = Solid::Get(Solid::ByFaces(4));
assert(myTetra->getName() == "Tetrahedron");
assert(myTetra->getFaces() == 4);
assert(myTetra->getNodes() == 4);
return 0;
} // main
And now we have met our goals:
Adding one new solid does not cause any recompilation
We can lookup solid based on their properties
We could also imagine:
being able to iterate through all the registered solids
having them sorted by number of faces, or whatever
defining a little macro for the registration
This is precisely what virtual functions are for. The classical way to do it would be:
class Topology
{
public:
virtual int nodesPerElement() const = 0;
// etc
};
class Tetrahedrom : public Topology
{
public:
virtual nodesPerElement() const { return 4; }
// etc
}
// etc
But if you really have an aversion to re-implementing the accessor methods (as opposed to just defining variables) you could do the following with templates (although it's really no less verbose):
class Topology
{
public:
virtual int nodesPerElement() const = 0;
// etc
};
template<typename T>
class ConcreteTopology : public Topology
{
public:
virtual int nodesPerElement() const { return T::nodesPerElement; }
// etc
};
struct Tetrahedron_Data {
int nodesPerElement = 4;
// etc
};
typedef ConcreteTypology<Tetraheadron_Data> Tetrahedron;
// etc