Without polymorphism
I have implemented an entity-component system that useses templates to get the components. An id is generated for each type. The function size_t GetComponentTypeId<T>() will always return the same id for a given type T.
For better understanding, here the function to add components
template <typename TComponent, typename... TArguments>
inline TComponent & Entity::AddComponent(TArguments&&... arguments)
{
// Check whether the component doesn't already exist
assert(componentBitSet[detail::GetComponentTypeID<TComponent>()] == false && "The component already exists");
assert(componentArray[detail::GetComponentTypeID<TComponent>()] == nullptr && "The component already exists");
TComponent * c = new TComponent(*this, std::forward<TArguments>(arguments)...);
Component::UPtr uPtr{ c };
componentList.emplace_back(std::move(uPtr));
// set the component * in the array
componentArray[detail::GetComponentTypeID<TComponent>()] = c;
// set the according component flag to true
componentBitSet[detail::GetComponentTypeID<TComponent>()] = true;
return *c;
}
And here the function to get components
template<typename TComponent>
inline TComponent & Entity::GetComponent() const
{
Component * component = componentArray[getComponentTypeID<TComponent>()];
if (component == nullptr)
throw std::runtime_error("Entity: This entity does not have the requested component");
return *static_cast<TComponent*>(component);
}
Nothing special here
Also my current implementation if the GetComponentTypeID() method:
namespace detail
{
typedef std::size_t ComponentTypeID;
/// #brief Returns a unique number (for each function call) of type std::size_t
inline ComponentTypeID GetComponentID() noexcept
{
// This will only be initialised once
static ComponentTypeID lastId = 0;
// After the first initialisation a new number will be returned for every function call
return lastId++;
}
/// #brief Returns a unique number (of type std::size_t) for each type T
/// #details Each component type will have its own unique id.
/// The id will be the same for every instance of that type
/// #tparam T The type for which the id is generated
template <typename T>
inline ComponentTypeID GetComponentTypeID() noexcept
{
// There will be only one static variable for each template type
static ComponentTypeID typeId = GetComponentID();
return typeId;
}
} // namespace detail
Adding polymorphism
Now I want to add polymorphic behaivour to my classes. E.g. there might be a SpriteRenderComponent which inherits from RenderComponent (which of course inherits Component). The RenderComponent would have a virtual draw method that is implemented in the SpriteRenderComponent. I want to be able to only add the sprite component and still be able to get the a reference to the renderComponent by calling entity.GetComponent<RenderComponent>() on the entity to which the sprite component has been added to. Calling the draw method on the returned render component reference should call SpriteRenderComponent.draw(). Also I should not be able to add any other components that inherit from render component.
Some of my thoughts
I think, the basic solution would be to add a pointer of the one instance of the SpriteRenderComponent for both ids; RenderComponent and SpriteRenderComponent. This would also prevent the user from adding multiple components that inherit from RenderComponent. The component itself would only be added once to the componentList and so only be updated once per frame (as wanted)
Problem: Making it typesafe
My problem is that I am struggeling to make it typesafe. Also I want to include some kind of check that makes sure that SpriteRenderComponent actually inherits from RenderComponent. My favourite solution would be one that 'automatically adds gets the superclass's ids and adds the component pointer for them as well. I am quite new to this kind of meta programming (perhaps the wrong word) so help would be much appreciated.
Update
One possible solution I found was to add an AddPolymorphism<class TDerivedComponent, class TBaseComponent>() method to the entity class. Here is the implementation:
template<class TDerivedComponent, class TBaseComponent>
inline void Entity::AddPolymorphism()
{
// Needed since std::is_base_of<T, T> == true
static_assert(std::is_base_of<Component, TBaseComponent>::value, "Entity: TBaseComponent must inherit from Component");
static_assert(std::is_same<Component, TBaseComponent>::value == false, "Entity: TBaseComponent must inherit from Component");
static_assert(std::is_base_of<TBaseComponent, TDerivedComponent>::value, "Entity: TDerivedComponent must inherit from TBaseComponent");
static_assert(std::is_same<Component, TBaseComponent>::value == false, "Entity: TBaseComponent must inherit from Component");
assert(this->HasComponent<TDerivedComponent>() && "Entity: The entity must have the derived component");
auto derivedComponentPtr = componentDictionary.find(detail::GetComponentTypeID<TDerivedComponent>())->second.lock();
componentDictionary.insert(std::make_pair(detail::GetComponentTypeID<TBaseComponent>(), derivedComponentPtr));
}
I guess its kinda typesafe but for me it has one major issue. It requires me to call this function every single time I add an component that has polymorphic behaivour. Although this is a solution (kinda) I'd much prefer a static way to specify this behaivour.
For the part about making sure it inherits from:
template<typename T>
struct Foo {
static_assert(is_base_of<Base, T>::value, "T must inherit from Base");
};
Might help you out there; as for the other questions; I will need more time as I have to soon leave... I'll come back to this later on when I get the chance to update this answer.
EDIT - Added some additional classes and show their uses.
I've had some time to work on something; I don't know for sure if this is what you are looking for; but this is a storage-manager type system that I've used before. It does support polymorphic behavior of classes. So maybe this structure will help you out.
main.cpp
#include <iostream>
#include <string>
#include <memory>
#include "FooManager.h"
#include "DerivedFoos.h"
int main() {
try {
std::unique_ptr<FooManager> pFooManager;
pFooManager.reset( new FooManager() );
for ( unsigned i = 0; i < 10; i++ ) {
DerivedA* pA = new DerivedA();
DerivedB* pB = new DerivedB();
pFooManager->add( pA, FOO_A );
pFooManager->add( pB, FOO_B );
}
pFooManager.reset();
} catch ( std::exception& e ) {
std::cout << e.what() << std::endl;
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return -1;
} catch ( std::string str ) {
std::cout << str << std::endl;
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return -1;
} catch ( ... ) {
std::cout << __FUNCTION__ << " caught unknown exception." << std::endl;
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return -1;
}
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return 0;
}
FooBase.h
#ifndef FOO_BASE_H
#define FOO_BASE_H
enum FooTypes {
FOO_A,
FOO_B,
FOO_UNKNOWN // MUST BE LAST!!!
};
class FooBase {
protected:
std::string _nameAndId;
private:
std::string _id;
static int _baseCounter;
public:
std::string idOfBase();
virtual std::string idOf() const = 0;
protected:
FooBase();
};
#endif // !FOO_BASE_H
FooBase.cpp
#include "FooBase.h"
#include <iostream>
#include <string>
int FooBase::_baseCounter = 0;
FooBase::FooBase() {
_id = std::string( __FUNCTION__ ) + std::to_string( ++_baseCounter );
std::cout << _id << " was created." << std::endl;
}
std::string FooBase::idOfBase() {
return _id;
}
std::string FooBase::idOf() const {
return "";
} // empty
DerivedFoos.h
#ifndef DERIVED_FOOS_H
#define DERIVED_FOOS_H
#include "FooBase.h"
class DerivedA : public FooBase {
private:
static int _derivedCounter;
public:
DerivedA();
std::string idOf() const override;
};
class DerivedB : public FooBase {
private:
static int _derivedCounter;
public:
DerivedB();
std::string idOf() const override;
};
#endif // !DERIVED_FOOS_H
DerivedFoos.cpp
#include "DerivedFoos.h"
#include <iostream>
#include <string>
int DerivedA::_derivedCounter = 0;
int DerivedB::_derivedCounter = 0;
DerivedA::DerivedA() : FooBase() {
_nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedA::_derivedCounter );
std::cout << _nameAndId << " was created." << std::endl;
}
std::string DerivedA::idOf() const {
return _nameAndId;
}
DerivedB::DerivedB() : FooBase() {
_nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedB::_derivedCounter );
std::cout << _nameAndId << " was created." << std::endl;
}
std::string DerivedB::idOf() const {
return _nameAndId;
}
FooManager.h - I'm not going to change the code for this class to replace its name. After looking at this for a little while; it became apparent that something like FooStore or Storage etc. would be a more suitable name for this class. It doesn't really manage anything other than the adding and removing of objects from its member container(s). You could keep its name as is if you decide to add more functionality that does more than just the add and remove objects.
#ifndef FOO_MANAGER_H
#define FOO_MANAGER_H
class FooBase;
class DerivedA;
class DerivedB;
enum FooTypes;
class FooManager final {
private:
static bool _alreadyExists;
typedef std::unordered_map<std::string, std::shared_ptr<FooBase>> MapFoos;
MapFoos _idsA;
MapFoos _idsB;
std::vector<std::string> _foosForRemoval;
public:
FooManager();
~FooManager();
// Foo Objects
FooBase* getFoo( const std::string& id, FooTypes type ) const;
void add( FooBase* foo, FooTypes type );
bool removeFoo( const std::string& id );
template<typename T>
bool removeFoo( T* pFoo );
void markFooForRemoval( const std::string& id );
private:
FooBase* getFoo( const std::string& id, const MapFoos& fooMap ) const;
void add( FooBase* pFoo, MapFoos& fooMap );
bool removeFoo( const std::string& strId, MapFoos& fooMap );
};
template<typename T>
inline bool FooManager::removeFoo( T* pFoo ) {
return false;
}
#endif // !FOO_MANAGER_H
FooManager.cpp
#include "FooManager.h"
#include "DerivedFoos.h"
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <memory>
bool FooManager::_alreadyExists = false;
FooManager::FooManager() {
// First check if no other instance is created.
if ( _alreadyExists ) {
std::ostringstream strStream;
strStream << "Failed to create " << __FUNCTION__ << " as it was already created." << std::endl;
throw strStream.str();
}
// Make sure this is last
_alreadyExists = true;
std::cout << __FUNCTION__ + std::string( " was created successfully." ) << std::endl;
}
FooManager::~FooManager() {
// If we are destroying make sure to reset flag
// So it can be constructed again.
_idsA.clear();
_idsB.clear();
_alreadyExists = false;
std::cout << __FUNCTION__ + std::string( " was destroyed successfully." ) << std::endl;
}
FooBase* FooManager::getFoo( const std::string& id, FooTypes type ) const {
switch ( type ) {
case FOO_A: {
return getFoo( id, _idsA );
}
case FOO_B: {
return getFoo( id, _idsB );
}
default: {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Unrecognized FooType = " << type;
throw strStream.str();
}
}
return nullptr;
}
FooBase* FooManager::getFoo( const std::string& id, const MapFoos& fooMap ) const {
MapFoos::const_iterator itFoo = fooMap.find( id );
if ( itFoo == fooMap.cend() ) {
return nullptr;
}
return itFoo->second.get();
}
void FooManager::add( FooBase* pFoo, FooTypes type ) {
// first check to see foo is valid
if ( nullptr == pFoo ) {
std::ostringstream strStream;
strStream << __FUNCTION__ + std::string( " pFoo == nullptr passed in" );
}
// Make Sure Name Is Unique Across All Foo Types
for ( int i = 0; i < FOO_UNKNOWN; ++i ) {
if ( getFoo( pFoo->idOf(), (FooTypes)i ) != nullptr ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " attempting to store " << pFoo->idOf() << " multiple times" << std::endl;
throw strStream.str();
}
}
switch ( type ) {
case FOO_A: {
add( pFoo, _idsA );
break;
}
case FOO_B: {
add( pFoo, _idsB );
break;
}
default: {
std::ostringstream strStream;
strStream << __FUNCTION__ << " uncrecognized FooType = " << type;
}
}
}
void FooManager::add( FooBase* pFoo, MapFoos& fooMap ) {
fooMap.insert( MapFoos::value_type( pFoo->idOf(), std::shared_ptr<FooBase>( pFoo ) ) );
}
template<>
bool FooManager::removeFoo( DerivedA* pFoo ) {
return removeFoo( pFoo->idOf(), _idsA );
}
template<>
bool FooManager::removeFoo( DerivedB* pFoo ) {
return removeFoo( pFoo->idOf(), _idsB );
}
bool FooManager::removeFoo( const std::string& id ) {
// Find which type this Foo is in
for ( int i = 0; i < FOO_UNKNOWN; ++i ) {
FooBase* pFoo = getFoo( id, (FooTypes)i );
if ( pFoo != nullptr ) {
// Found It
switch ( static_cast<FooTypes>(i) ) {
case FOO_A: {
return removeFoo( pFoo->idOf(), _idsA );
}
case FOO_B: {
return removeFoo( pFoo->idOf(), _idsB );
}
default: {
std::ostringstream strStream;
strStream << __FUNCTION__ << " uncrecognized FooType = " << i;
throw strStream.str();
}
}
}
}
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed. " << id << " was not found in FooManager";
// don't throw just display message (typically write to log file).
std::cout << strStream.str() << std::endl;
return false;
}
bool FooManager::removeFoo( const std::string& id, MapFoos& fooMap ) {
MapFoos::iterator itFoo = fooMap.find( id );
if ( itFoo == fooMap.end() ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed. " << id << " was not found in AssetStorage";
// don't throw just display message (typically write to log file).
std::cout << strStream.str() << std::endl;
return false;
} else {
// do what ever from Foo's functions to clean up its internals
// itFoo->second.get()->cleanUp(); // etc.
fooMap.erase( itFoo );
// When the above foo was deleted, there might have been some children
// that were also marked for removal. We can remove them here.
for ( unsigned i = 0; i < _foosForRemoval.size(); ++i ) {
itFoo = _idsB.find( _foosForRemoval[i] );
if ( itFoo != _idsB.end() ) {
// Remove this Foo
// do what ever from Foo's functions to clean up its internals.
// itFoo->second.get()->cleanUp(); // etc.
_idsB.erase( itFoo );
} else {
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed to find " << _foosForRemoval[i] << " for removal from the _idsB";
// don't throw just display message (typically write to log file).
std::cout << strStream.str() << std::endl;
}
}
_foosForRemoval.clear();
return true;
}
}
void FooManager::markFooForRemoval( const std::string& id ) {
_foosForRemoval.push_back( id );
}
It's a nice way to store items dynamically and yes you can see that I'm using new on the pointers in main, but you never see me using delete. This is because once we add that pointer to the manager class, it takes over and handles all the memory for us, since it will turn them into shared_ptr<T>. This manager class also supports polymorphic behavior. This is just a basic shell or structure.
Then from here. You can write another class that holds a pointer to this storage or manager class that will then add and remove items from these containers. The other class would be responsible for finding the objects in this storage and then invoking the methods of the internally stored objects; or you could just add all of that functionality directly into this class. I kind of like to try and keep the storage of things separate from the implementation of things. I hope this structure helps you out, or gives you some ideas to work off of. You can see that I did use function templates within this class to access specific maps of specific derived foos.
You should be able to integrate into the above classes the concept of the is_derived_from as well as checking to see if a specific item already exists and if does don't add it. Final note: you could also split the storage into 2 types where one container will be able to add multiple components that can be rendered multiple times per frame, while the other container can be restrictive. Not sure what kind of benefit you could get with that, maybe in a particle generator or engine system, but the flexibility is there to do that if you need it.
You just need to make detail::GetComponentTypeID<T>() smarter.
You have, in practice, a list of component types.
template<class...>
struct type_list_t {};
using ComponentList = type_list_t<RenderComponent, PhysicsComponent, CharmComponent>;
this list determines how long your pointer and bit flag arrays are. Place this list explicitly in a famous location that everyone knows about.
Yes, this means you have to rebuild if it changes. Tough.
Now you just have to improve detail::GetComponentTypeID<T>(). Have it constexpr or template metaprogramming search the ComponentList for the first type that passes std::is_base_of< ListElement, T >.
And your code now works as written.
Related
What's the consistency of the following codes:-
TMAP.h
#include <algorithm>
#include <map>
template <class K, class V>
class TMAP
{
private:
std::map <K,V> map_K_V;
public:
bool key_exists(const K& key) { return map_K_V.count( key ) > 0; }
bool insert(const K& key, const V& value)
{
if (!key_exists(key))
{
if (map_K_V.insert( std::make_pair( key, value ) ).second)
{
return true;
}
}
return false;
}
V get_value(const K& key)
{
return map_K_V[ key ];
}
};
Template just like std::map, just more organized for other uses.
main.cpp
#include <iostream>
#include "TMAP.h"
class A;
TMAP< std::string, A* > map_cntr;
class A
{
public:
A( std::string nm )
{
name = nm;
std::cout << "A: " << name << ", object created." << std::endl;
}
~A()
{
std::cout << "A: " << name << ", object destroyed." << std::endl;
}
void printName()
{
std::cout << "A: printName - " << name << std::endl;
}
void setName( std::string nm )
{
name = nm;
}
private:
std::string name;
};
int main() {
// Setting
A* obj1 = new A( "obj1" );
map_cntr.insert( "obj1", obj1 );
obj1->printName();
A* obj2 = new A( "obj2" );
map_cntr.insert( "obj2", obj2 );
obj2->printName();
// Getting
A* obj1_cpy;
std::string obj1_name = "obj1";
if (map_cntr.key_exists(obj1_name))
{
obj1_cpy = map_cntr.get_value(obj1_name);
obj1_cpy->printName();
obj1_cpy->setName("OBJ1");
obj1_cpy->printName();
}
}
Outputs:
A: obj1, object created.
A: printName - obj1
A: obj2, object created.
A: printName - obj2
A: printName - obj1
A: printName - OBJ1
Outputs are as expected. Besides I've heard somewhere that using std::string as template parameter is not ideal just like in the above case somewhat relating to memory or pointer. Is it fair?
It is std::map<const char*, Value> which is "problematic", as it compare only pointers and not C-string content.
Using std::map<std::string, Value> is fine.
In some words: how can I pass various fields from a custom class to a single function?
Now in details:
I have a std::vector containing a class, for example CustomClass from which I have to extract a result from a field from this class by some criteria which are fields in this class and to combine somehow this data.
My first approach to this problem was to use a function which accepts as a parameter the std::vector of the class in order to extract the data and return a std:map. The key in this map is the type of the criteria by which the data should be combined and the value is an int with the combined data from all members of this vector.
The problem is that the criteria is not only one - more than one field from this class may be used as criteria (let for easiness all of the criteria are std::string, if they are not - I could make the function templated).
The easiest way for me now is to make dozens of functions with almost identical code and each of them to extract a simple concrete field from this class. However changes might require similar changes to all of the dozens of functions which would be a maintenance headache. But in this stage I cannot think how to pass to a single function a field from this class...
Here's an example code from this class:
// this is the class with data and criteria
class CustomClass
{
public:
std::string criteria1;
std::string criteria2;
std::string criteria3;
//... and others criteria
int dataToBeCombined;
// other code
};
// this is one of these functions
std::map<std::string, int> getDataByCriteria1(std::vector<CustomClass> aVector)
{
std::map<std::string, int> result;
foreach(CustomClass anObject in aVector)
{
if(result.find(anObject.criteria1)==result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(anObject.criteria1, anObject.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
and by similar way I should make the other functions which should work with CustomClass::criteria2, CustomClass::criteria3, etc.
I thought to make these criteria in a single array and to pass to this function only the number of the criteria but the class will be used by others for other purposes and the fields must be easy to read, so this will not be an option (i.e. the real names are not criteria1, criteria2, etc. but are descriptive).
Anyone with ideas?
EDIT: Someone referred my question to "C++ same function parameters with different return type" which obviously is very different - the function in my case return the same type every time, just the parameters it takes must be various fields from a class.
You can use pointer to member. Declare an argument std::string CustomClass::*pField in your function, pass it with &CustomClass::criteriaN, access it with anObject.*pField.
See more on the topic: Pointers to data members.
If all "criteria" are of the same type, I don't see an elegant solution but you can "enumerate" they in some way and use their number.
By example, you can declare a templated getVal() method in CustomClass in this way
template <int I>
const std::string & getVal () const;
and implement they, number by number, criteria by criteria, in this way (outside the body of the class)
template <>
const std::string & CustomClass::getVal<1> () const
{ return criteria1; }
template <>
const std::string & CustomClass::getVal<2> () const
{ return criteria2; }
template <>
const std::string & CustomClass::getVal<3> () const
{ return criteria3; }
Now, you can transform getDataByCriteria1() in a templated function getDataByCriteria() in this way
template <int I>
std::map<std::string, int> getDataByCriteria (std::vector<CustomClass> aVector)
{
std::map<std::string, int> result;
for (const auto & cc : aVector)
{
if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
and call it in this way
auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);
--- EDIT: added solution (C++14 only) for different types criteria ---
A little different if the "criteria" are of different types.
The solution work but in C++14, thanks to auto and decltype().
By example, if
std::string criteria1;
int criteria2;
long criteria3;
You can declare getVal() with auto
template <int I>
const auto & getVal () const;
and define (with auto) all versions of getVal()
template <>
const auto & CustomClass::getVal<1> () const
{ return criteria1; }
template <>
const auto & CustomClass::getVal<2> () const
{ return criteria2; }
template <>
const auto & CustomClass::getVal<3> () const
{ return criteria3; }
and combining auto with decltype(), you can modify getDataByCriteria() in this way
template <int I>
auto getDataByCriteria (std::vector<CustomClass> aVector)
{
std::map<decltype(aVector[0].getVal<I>()), int> result;
for (const auto & cc : aVector)
{
if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
{
result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
}
}
return result;
}
The use of the function remain the same (thanks to auto again)
auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);
p.s.: caution: code not tested
p.s.2 : sorry for my bad English
You can use a function to extract a filed such as
std::string extractFiled(const CustomClass &object, int which) {
switch (which) {
case 1:
return object.criteria1;
case 2:
return object.criteria2;
case 3:
return object.criteria3;
default:
return object.criteria1;
}
}
and getDataByCriteria add an arg to indicate which filed to use.
Or you can just use macro to implement getDataByCriteria.
You tagged it C++11, so use variadic templates.
class VariadicTest
{
public:
VariadicTest()
{
std::map<std::string, int> test1 = getDataByCriteria(testValues, criteria1);
std::map<std::string, int> test2 = getDataByCriteria(testValues, criteria2);
std::map<std::string, int> test3 = getDataByCriteria(testValues, criteria1, criteria2);
std::map<std::string, int> test4 = getDataByCriteria(testValues, criteria1, criteria3);
}
private:
std::string criteria1 = { "Hello" };
std::string criteria2 = { "world" };
std::string criteria3 = { "." };
std::vector<CustomClass> testValues = { {"Hello",1}, {"world",2},{ "!",3 } };
template<typename T> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T criteria)
{
std::map<std::string, int> result;
//do whatever is needed here to filter values
for (auto v : values)
{
if (v.identifier == criteria)
{
result[values[0].identifier] = values[0].value;
}
}
return result;
}
template<typename T, typename... Args> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T firstCriteria, Args... args)
{
std::map<std::string, int> result = getDataByCriteria(values, firstCriteria);
std::map<std::string, int> trailer = getDataByCriteria(values, args...);
result.insert(trailer.begin(), trailer.end());
return result;
}
};
You do not specify the actual operations to be done under the various conditions of the criteria being met so it is hard to say how much they actually can be combined.
Here is a possible solution using the std::accumulate() of the STL along with some additional functionality. This example was compiled with Visual Studio 2015.
This approach would make sense if most of the functionality can be combined into a reasonably small accumulation function because most of the criteria are handled in the same way. Or you could have the accumulate_op() function call other functions for specific cases while handling the general case itself.
You might take this as a beginning and make the appropriate modifications.
One such modification may be to get rid of the use of std::map to maintain state. Since using this approach you would iterate through the std::vector doing the accumulation based on the criteria, I am not sure you would even need to use std::map to remember anything if you are accumulating as you go.
// map_fold.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <numeric>
// this is the class with data and criteria
class CustomClass
{
public:
CustomClass() : dataToBeCombined(0) {}
std::string criteria1;
std::string criteria2;
std::string criteria3;
//... and others criteria
int dataToBeCombined;
// other code
};
// This is the class that will contain the results as we accumulate across the
// vector of CustomClass items.
class Criteria_Result {
public:
Criteria_Result() : dataToBeCombined(0) {}
CustomClass myCriteria;
std::map<std::string, int> result1;
std::map<std::string, int> result2;
std::map<std::string, int> result3;
int dataToBeCombined;
};
// This is the accumulation function we provide to std::accumulate().
// This function will build our results.
class accumulate_op {
public:
Criteria_Result * operator ()(Criteria_Result * x, CustomClass &item);
};
Criteria_Result * accumulate_op::operator ()(Criteria_Result *result, CustomClass &item)
{
if (!result->myCriteria.criteria1.empty() && !item.criteria1.empty()) {
std::map<std::string, int>::iterator it1 = result->result1.find(item.criteria1);
if (it1 == result->result1.end()) // if such of key doesn't exists
{
result->result1.insert(std::make_pair(item.criteria1, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it1->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
if (!result->myCriteria.criteria2.empty() && !item.criteria2.empty()) {
std::map<std::string, int>::iterator it2 = result->result2.find(item.criteria2);
if (it2 == result->result2.end()) // if such of key doesn't exists
{
result->result2.insert(std::make_pair(item.criteria2, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it2->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
if (!result->myCriteria.criteria3.empty() && !item.criteria3.empty()) {
std::map<std::string, int>::iterator it3 = result->result3.find(item.criteria3);
if (it3 == result->result3.end()) // if such of key doesn't exists
{
result->result3.insert(std::make_pair(item.criteria3, item.dataToBeCombined));
}
else
{
// do some other stuff in order to combine data
it3->second += item.dataToBeCombined;
}
result->dataToBeCombined += item.dataToBeCombined;
}
return result;
}
int main()
{
Criteria_Result result;
std::vector<CustomClass> aVector;
// set up the criteria for the search
result.myCriteria.criteria1 = "string1";
result.myCriteria.criteria2 = "string2";
for (int i = 0; i < 10; i++) {
CustomClass xx;
xx.dataToBeCombined = i;
if (i % 2) {
xx.criteria1 = "string";
}
else {
xx.criteria1 = "string1";
}
if (i % 3) {
xx.criteria2 = "string";
}
else {
xx.criteria2 = "string2";
}
aVector.push_back (xx);
}
// fold the vector into our results.
std::accumulate (aVector.begin(), aVector.end(), &result, accumulate_op());
std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
std::cout << " result1 list " << std::endl;
for (auto jj : result.result1) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result2 list " << std::endl;
for (auto jj : result.result2) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result3 list " << std::endl;
for (auto jj : result.result3) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " Trial two \n\n" << std::endl;
result.myCriteria.criteria2 = "";
result.result1.clear();
result.result2.clear();
result.result3.clear();
result.dataToBeCombined = 0;
// fold the vector into our results.
std::accumulate(aVector.begin(), aVector.end(), &result, accumulate_op());
std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
std::cout << " result1 list " << std::endl;
for (auto jj : result.result1) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result2 list " << std::endl;
for (auto jj : result.result2) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
std::cout << " result3 list " << std::endl;
for (auto jj : result.result3) {
std::cout << " " << jj.first << " " << jj.second << std::endl;
}
return 0;
}
This produces the output as follows:
Total Data to be combined 90
result1 list
string 25
string1 20
result2 list
string 27
string2 18
result3 list
Trial two
Total Data to be combined 45
result1 list
string 25
string1 20
result2 list
result3 list
PART OF A HOMEWORK PROBLEM
I have a list of objects, and my goal is to try and find if an object X is present in that list (I am interested only in the first occurrence of that object). My code seems to work fine for the most part, but I have this strange error where the value of only 1 particular object is being modified after it is returned from a function.
I added 10 objects to the list with values 0 through 3. When I search for any number except 0, (./a.out 1 OR ./a.out 2 and so on)I get the right output. But when I search for 0(./a.out 0), the findInList() prints the correct result, but the main() prints the value 18 (which is not even present in the list).
I am attaching the full source code here in case someone wants to compile it and try it out. I am also attaching the gdb step through I did.
SOURCE:
#include <iostream>
#include <string>
#include <functional>
#include <list>
using namespace std;
class Page {
public:
int pgnum; // page number
union {
int lfu_count;
int lru_clock:1; // can only be 0/1
int lru_ref8:8; // we only need 8 bits
};
public:
// Constructors
Page(int num) { pgnum = num; }
Page() {}
// Operator overloading
bool operator== (const Page &p) const {
if(p.pgnum == pgnum)
return true;
else
return false;
}
bool operator!= (const Page &p) const {
return !(p==*this);
}
};
ostream & operator<<(ostream & os, const Page &p) {
os << "Page number: " << p.pgnum;
return os;
}
// Think of this as an equivalent to equals in Java (IT IS NOT, JUST IMAGINE)
struct PageNumber: public binary_function< Page, Page, bool > {
bool operator () ( const Page &p1, const Page &p2 ) const {
return p1 == p2;
}
};
// Function to find an object in any list given an Operation
template <class Operation, class T>
T* findInList( list<T> fullList, T obj, const Operation &op ) {
T* ret = NULL;
typename list<T>::iterator it = fullList.begin();
it = find_if( it, fullList.end(), bind2nd( op, obj ) );
if( it != fullList.end() ) {
cout << "Found obj in list: " << *it << endl;
ret = &(*it); // not the same as it (which is of type iterator)
}
return ret;
}
int main( int argc, char **argv ) {
Page page_to_find;
list<Page> frames;
if( argc != 2 ) {
cout << "Please enter 1 and only 1 argument" << endl;
exit(-1);
}
page_to_find.pgnum = atoi(argv[1]);
Page *p = new Page[10];
for( int i=0; i<10; i++ ) {
p[i].pgnum = i%4;
frames.push_back(p[i]);
}
list<Page>::iterator it_frames = frames.begin();
while( it_frames != frames.end() ) {
cout << "Page in frames: " << *it_frames << endl;
it_frames++;
}
Page* pg = findInList( frames, page_to_find, PageNumber() );
if( pg != NULL )
cout << "Found page: " << *pg << endl;
delete[] p;
return 0;
}
You're returning the address of an object in a list that is pushed into the parameter list by value. Thus it is undefined behavior. Consider changing the parameter of the list in findInList to a reference.
// note reference type change in parameter list.
template <class Operation, class T>
T* findInList( list<T>& fullList, T obj, const Operation &op ) {
T* ret = NULL;
typename list<T>::iterator it = fullList.begin();
it = find_if( it, fullList.end(), bind2nd( op, obj ) );
if( it != fullList.end() ) {
cout << "Found obj in list: " << *it << endl;
ret = &(*it); // not the same as it (which is of type iterator)
}
return ret;
}
How to implement a stack using array that supports items of different type.
e.g. It should operate on characters, integers, floats and doubles.
I have implemented it using void pointers. Below is the C implementation:
void push( void** stack, int* top, void* data, size_t size )
{
unsigned i;
++*top;
stack[*top] = malloc( size );
for( i = 0; i < size; ++i )
( (char*)stack[*top] )[i] = ( (char*)data )[i];
}
int main()
{
void* stack[10];
int top = -1, data = 10;
char ch = 'a';
push( stack, &top, (void*)&data, sizeof( int ) );
push( stack, &top, (void*)&ch, sizeof( char ) );
printf( "%d ", *(int*)stack[0] );
printf( "%c ", *(char*)stack[1] );
return 0;
}
The code works fine for me.
The problem with the above implementation is that the type of data must be known before-hand.
Does there exists a way to implement it without knowing prior information about type of data to be operated [ I know it is not possible in C, Can we do it in C++, if Yes, How? ]?
It can be done in C. First you need an enum describing the contained type
typedef enum {TYPE_INT, TYPE_CHAR, TYPE_STRING, ... } contained_type_t;
Then put everything in a structure
typedef struct {
contained_type_t contained_type;
union {
int int_value;
char char_value;
void* pointer_value;
... and so on ...
} data;
};
You could take a look at the GObject model and its uses in the GLib, the implementation of generic lists which can mix types is interesting.
If I understand your requirement, you can use boost::any to achieve this in C++.
#include <boost/any.hpp>
#include <vector>
class AnyStack {
std::vector<boost::any> vec;
public:
template <class T> void push (const T &e) {
boost::any v = e;
vec.push_back(v);
}
class Proxy {
friend class AnyStack;
std::vector<boost::any> &vec;
Proxy(std::vector<boost::any> &v) : vec(v) {}
public:
template <typename T> operator T () {
boost::any v = vec.back();
vec.pop_back();
return boost::any_cast<T>(v);
}
};
Proxy pop () { return Proxy(vec); }
boost::any top () { return vec.back(); }
};
As others have suggested, you can use RTTI to determine the type of the item in the stack. The example below demonstrates an output routine that does this.
#include <iostream>
#include <string>
std::ostream & operator << (std::ostream &os, const boost::any &a) {
if (a.type() == typeid(char)) {
return os << boost::any_cast<char>(a);
}
if (a.type() == typeid(int)) {
return os << boost::any_cast<int>(a);
}
if (a.type() == typeid(float)) {
return os << boost::any_cast<float>(a);
}
if (a.type() == typeid(double)) {
return os << boost::any_cast<double>(a);
}
if (a.type() == typeid(std::string)) {
return os << boost::any_cast<std::string>(a);
}
}
int main () {
AnyStack a;
a.push(3);
std::cout << a.top() << std::endl;
a.push(std::string("hello"));
std::cout << a.top() << std::endl;
return 0;
}
I'm currently writing a program that has debug output strewn throughout it. This is all well and good, but I'd like to be able to advance the tab position for things in different scopes, for instance, this is what I have right now:
#ifndef NDEBUG
printf("Updating player\n");
#endif
player.Update();
#ifndef NDEBUG
printf("Done updating player\n");
#endif
I'd like to have so that all the output called between these two blocks is advanced by one tab position; however, simply adding tabs to the beginning of Player::Update() output is incredibly clunky AND difficult to maintain.
Anybody got any help? (Note: I have no problem with using cout instead; I was just recently lectured about the overhead and insecurity with cout)
Instead of using printf directly, create a class to handle console output and have a member function such as SetTabLevel() -- and perhaps also IncrementTabLevel() and DecrementTabLevel() -- which tells the class how many tabs to add at the beginning of each subsequent line.
You could have a class that essentially maintained a "tab count" and had a print_line function: when called, it would output tab-count tabs, and then print the line. While you could have a increment_indent function, you could create a sister object TabIndent using RAII: When it is created, increment the tab, when it is destructed, decrement the indent:
some_function()
{
TabIndent ti(global_debug_outputter);
global_debug_outputted.print_line("foo bar baz");
// ti's d-tor calls global_debug_outputted.unindent()
}
class TabIndent
{
public:
TabIndent(Outputter &o) : m_outputter(o)
{
o.indent();
}
~TabIndent()
{
o.unindent();
}
};
class Outputter
{
// functions indent, unindent, print_line...
};
Good use of inlines will let the compiler optimize them out when their bodies are empty. Use #ifndef in the Outputter class, and let the compiler optimize the rest out.
The good news is that C++ iostreams are very customizable. The bad news is that the interface is a bit weird.
#include <iostream>
class scoped_streambuf : public std::streambuf {
std::streambuf *sb;
size_t tabs;
bool at_nl;
virtual int_type overflow( int_type c = traits_type::eof() ) {
if ( at_nl ) for ( size_t t = 0; t < tabs; ++ t ) {
int_type r = sb->sputc( '\t' );
if ( r == traits_type::eof() ) return r;
}
int_type r = sb->sputc( c );
if ( r == traits_type::eof() ) return r;
at_nl = c == '\n';
return c;
}
virtual int sync() { return sb->pubsync(); }
static void uninstall( std::ios_base::event what, std::ios_base &ios, int ) {
if ( what != std::ios_base::erase_event ) return;
std::ostream &os = dynamic_cast< std::ostream & >( ios );
scoped_streambuf *this_ = static_cast< scoped_streambuf * >( os.rdbuf() );
os.rdbuf( this_->sb );
delete this_;
}
public:
scoped_streambuf( std::ostream &inos )
: sb( inos.rdbuf( this ) ), tabs(), at_nl() {
inos.register_callback( &uninstall, 0 );
}
friend std::ostream &indent( std::ostream &os ) {
++ dynamic_cast< scoped_streambuf & >( * os.rdbuf() ).tabs;
return os;
}
friend std::ostream &outdent( std::ostream &os ) {
-- dynamic_cast< scoped_streambuf & >( * os.rdbuf() ).tabs;
return os;
}
};
std::ostream &indent( std::ostream & );
std::ostream &outdent( std::ostream & );
struct indent_scope {
std::ostream &os;
indent_scope( std::ostream &inos ) : os( inos ) { os << indent; }
~indent_scope() { os << outdent; }
};
int main() {
new scoped_streambuf( std::cout );
std::cout << "hello\n";
{
indent_scope s( std::cout );
std::cout << "world" << std::endl;
}
std::cout << "!\n";
}
I checked that the scoped_streambuf indeed deletes itself when the associated stream is destroyed, but apparently std::cout itself is never destroyed on GCC.
Level-up iostream fu, whee!