I have a large code base, originally C ported to C++ many years ago, that is operating on a number of large arrays of spatial data. These arrays contain structs representing point and triangle entities that represent surface models. I need to refactor the code such that the specific way these entities are stored internally varies for specific scenarios. For example if the points lie on a regular flat grid, I don't need to store the X and Y coordinates, as they can be calculated on the fly, as can the triangles. Similarly, I want to take advantage of out of core tools such as STXXL for storage. The simplest way of doing this is replacing array access with put and get type functions, e.g.
point[i].x = XV;
becomes
Point p = GetPoint(i);
p.x = XV;
PutPoint(i,p);
As you can imagine, this is a very tedious refactor on a large code base, prone to all sorts of errors en route. What I'd like to do is write a class that mimics the array by overloading the [] operator. As the arrays already live on the heap, and move around with reallocs, the code already assumes that references into the array such as
point *p = point + i;
may not be used. Is this class feasible to write? For example writing the methods below in terms of the [] operator;
void MyClass::PutPoint(int Index, Point p)
{
if (m_StorageStrategy == RegularGrid)
{
int xoffs,yoffs;
ComputeGridFromIndex(Index,xoffs,yoffs);
StoreGridPoint(xoffs,yoffs,p.z);
} else
m_PointArray[Index] = p;
}
}
Point MyClass::GetPoint(int Index)
{
if (m_StorageStrategy == RegularGrid)
{
int xoffs,yoffs;
ComputeGridFromIndex(Index,xoffs,yoffs);
return GetGridPoint(xoffs,yoffs); // GetGridPoint returns Point
} else
return m_PointArray[Index];
}
}
My concern is that all the array classes I've seen tend to pass by reference, whereas I think I'll have to pass structs by value. I think it should work put other than performance, can anyone see any major pitfalls with this approach. n.b. the reason I have to pass by value is to get
point[a].z = point[b].z + point[c].z
to work correctly where the underlying storage type varies.
You should not need to pass the array by value. For mutating the values in the array, you want two versions of operator[], one which returns a reference (to mutate) and one a const reference.
There is no reason in principle not to use operator[], as long as you do not need to vary the type of the storage at run time - there are no virtual operators, so you would need a named function if you want runtime polymorphism. In that case, you can create a simple struct which adapts the operator calls to function calls (though it rather depends on the storage API - if the code assumes that assigning to the point's member variables changes the stored data, you might have to make the point type a template variable too so this can be overridden).
Looking at your sample code, it has a test for the storage strategy. Do not do this. Either use OO and have your storage object implement a common virtual interface, or (probably better) use template programming to vary the storage mechanism.
If you look at the guarantees made by std::vector (in more recent C++ standards), then it is possible to have something which has dynamic storage and allows use of pointer arithmetic, though that requires contiguous storage. Given that some of your values are created on the fly, it is probably not worth placing that restriction on your implementations, but the constraint itself does not prevent use of operator[].
What you want is possible, but as you need write access as well, the result will be a little bit more complex sometimes. What you want is the setter function returning not a direct "Point write access", rather a temporary copy, which will do the write once the copy goes out of the scope.
Following code fragment tries to outline the solution:
class PointVector
{
MyClass container_;
public:
class PointExSet: public Point
{
MyClass &container_;
int index_;
public:
PointExSet(MyClass &container, int index)
:Point(container.GetVector(index)),container_(container),index_(index)
{
}
~PointExSet()
{
container_.PutVector(index_) = *this;
}
};
PointExSet operator [] (int i)
{
return PointExSet(container_,i);
}
};
It is not as nice as you would probably hope it to be, but I am afraid you cannot get a much better solution in C++.
To have a full control over operations on array, operator[] should return a special object (invented long ago and called "cursor") that will handle operations for you.
As an example:
class Container
{
PointCursor operator [] (int i)
{
return PointCursor(this,i);
}
};
class PointCursor
{
public:
PointCursor(_container, _i)
: container(_container), i(_i),
//initialize subcursor
x(container, i) {}
//subcursor
XCursor x;
private:
Container* container;
int i;
};
class XCursor
{
public:
XCursor(_container, _i)
: container(_container), i(_i) {}
XCursor& operator = (const XCursor& xc)
{
container[i].x = xc.container[xc.i].x;
//or do whatever you want over x
}
Container* container;
int i;
}
//usage
my_container[i].x = their_container[j].x; //calls XCursor::operator = ()
After reading the above answers, I decided that Pete's answer with two versions of operator[] was the best way forward. To handle the morphing between types at run-time I created a new array template class that took four parameters as follows;
template<class TYPE, class ARG_TYPE,class BASE_TYPE, class BASE_ARG_TYPE>
class CMorphArray
{
int GetSize() { return m_BaseData.GetSize(); }
BOOL IsEmpty() { return m_BaseData.IsEmpty(); }
// Accessing elements
const TYPE& GetAt(int nIndex) const;
TYPE& GetAt(int nIndex);
void SetAt(int nIndex, ARG_TYPE newElement);
const TYPE& ElementAt(int nIndex) const;
TYPE& ElementAt(int nIndex);
// Potentially growing the array
int Add(ARG_TYPE newElement);
// overloaded operator helpers
const TYPE& operator[](int nIndex) const;
TYPE& operator[](int nIndex);
CBigArray<BASE_TYPE, BASE_ARG_TYPE> m_BaseData;
private:
CBigArray<TYPE, ARG_TYPE> m_RefCache;
CBigArray<int, int&> m_RefIndex;
CBigArray<int, int&> m_CacheIndex;
virtual void Convert(BASE_TYPE,ARG_TYPE) = 0;
virtual void Convert(TYPE,BASE_ARG_TYPE) = 0;
void InitCache();
TYPE& GetCachedElement(int nIndex);
};
The main data storage is in m_BaseData which is the data in its native format, which can vary in type as discussed. m_RefCache is secondary array to cache of elements in the expected format, and the GetCachedElement function uses the virtual Convert functions to translate the data as it is moved in and out of the cache. The cache needs to be at least as big as the number of simultaneous references that can be active at any one time, but in my case will probably benefit from being bigger as it reduces the number of conversions required. While Alsk's cursor implementation probably would have worked well, the solution given requires fewer object copies and temporary variables, and ought to afford slightly better performance which is important in this case.
Apologies to all you STL fans for the older MFC look and feel; the rest of the project is MFC so it makes more sense in this case. The CBigArray was the result of a related stack overflow question that became the basis of my large array handling. I hope to finish the implementation today and test tomorrow. If it all goes belly up on me, I'll edit this post accoringly.
Related
Let's say I have the following:
struct MetadataThingy {
void *actual_thingy;
int some_metadata;
int more_metadata;
bool operator<(MetadataThingy const& other) const {
return actual_thingy < other.actual_thingy;
}
};
where actual_thingy points to some data of importance and I want the container ordered by the value of actual_thingy rather than the value of the element pointed at, but I need to store some other data about it, so I created the wrapper class MetadataThingy with a comparator that only considers the value of the actual_thingy pointer (rather than using a container of void *)
Now, given the following code:
std::set<MetadataThingy> thingy_set;
void test() {
MetadataThingy m1 { nullptr, 5, 20 };
MetadataThingy m2 { &m1, 1, 2 };
MetadataThingy m3 { &m2, 6, 0 };
thingy_set.insert(m1);
thingy_set.insert(m2);
thingy_set.insert(m3);
MetadataThingy m;
m = *thingy_set.find(m2); // OK.
m = *thingy_set.find(static_cast<void *>(&m2)); // Nope. Can't use a pointer.
}
Since each MetadataThingy can be uniquely identified by the pointer value it stores and is ordered by the pointer value, it would make sense to find/delete objects simply by using a void * as the key. As it currently stands, though, I would have to create a dummy MetadataThingy each time I search for an element, which feels really kludgy. I've already considered using just a map with pointers as key and MetadataThingy as value but since each MetadataThingy must also contain the pointer anyway, this feels a bit redundant. So, is there a way to use an element of a type other than that stored in a set to find or delete values in the set, given that elements of the two types are mutually comparable and that elements of one type can be uniquely mapped into the other ( void * and MetadataThingy are isomorphic)? (I didn't include any in the above code, but suppose there are operator overloads for comparing void * and MetadataThingy in any order.)
A little background on the problem I'm trying to solve, just in case anyone can recommend a better approach: I need to order a collection by multiple criteria, so I have several MetadataThingy containers, all sorted by different criteria. "Metadata" in this case would be stuff I need to track the positions of the elements in all containers so that I can do fast removal. This would sound like a perfect job for boost multi-index containers, but the ordering of these elements is constantly changing, which AFAIK would mean it won't work.
As of C++14, std::set has templated versions of its lookup functions find, lower_bound, etc. They allow you to pass any object for comparison, as long as the comparer supports it.
This means you can directly pass your void* to find, as long as the comparer supports comparing MetadataThingy and void*.
For more information, see http://en.cppreference.com/w/cpp/container/set/find.
To understand the limitation regarding Compare::is_transparent, I found this StackOverflow question very helpful.
You can do this by using std::find_if and providing a predicate functor.
#include <algorithm>
struct Predicate
{
void const * const ptr_;
explicit Predicate(const void* ptr) : ptr_(ptr) {}
bool operator()(const MetadataThingy& other)
{
return ptr_ == other.actual_thingy;
}
};
m = *std::find_if(thingy_set.begin(), thingy_set.end(), Predicate(&m2));
You can use the iterator returned by std::find_if to remove the element from the set by passing it to set::erase.
No, the signature of map<>::find requires you pass in the key type.
There is, however, a relatively simple workaround. Use boost::optional or std::tr2::optional (from C++1y) to store your non-key data.
struct MetadataThingy {
void* pBlah;
optional<rest_of_stuff> rest;
static MetadataThingy searcher( void* );
MetadataThingy(...);
};
then call MeatadataThingy::searcher to generate your key value.
Another approach would be to store smart (unique probably) pointers to sub-interfaces, each of which has a "get full data" method. Then, when you want to do a search, create a stub sub-interface that returns nullptr on "get full data".
struct MetadataFull;
struct MetadataRoot {
virtual MetadataFull* get() = 0;
virtual MetadataFull const* get() const = 0;
virtual ~MetadataRoot() {}
};
template<typename T>
struct MetadataFinal: virtual MetadataRoot {
static_assert( std::is_base_of< T, MetadataFinal<T> >::value, "CRTP failure" );
virtual MetadataFull* get() { return static_cast<T*>(this); }
virtual MetadataFull const* get() const { return static_cast<T const*>(this); }
};
struct MetadataStub: virtual MetadataRoot {
virtual MetadataFull* get() { return nullptr; }
virtual MetadataFull const* get() const { return nullptr; }
};
struct MetaDataA: virtual MetaDataRoot {
void* pBlah;
};
struct MetaDataFull: MetaDataA, MetadataFinal<MetaDataFull> {
// unsorted data
};
struct MetaDataAStub: MetaDataA, MetaDataStub {};
now, this can be done with virtual functions but not virtual inheritance with a bit of finagling if you really need it.
The library does not support the behavior that you ask for, although I have seen other people request the same thing (i.e. providing a templated member function find in ordered associative containers that would use a cross comparator), although this is infrequent.
Your type is unusual in that only one of the member attributes takes part on the value of the object (i.e. is used in the comparison). The compiler cannot know that only some of the members (or which of the members) are part of the value and which are not. although it might be that the objects are not really comparable and you just hammered operator< as a simple way of enabling the use in associative containers.
If that is the case, consider dropping the operator< that does not really compare the MetaThingy objects and also change the data structure to be a std::map<void*,MetaThingy>, which would make the design cleaner at the cost of an extra void* per stored object --it might also be the case that the void* is inside the MetaThingy for the lookup in the set... in which case it might even make more sense and you could provide std::map<void*,MetaInfo>.
I have the following problem and I wonder whether there's a better way to solve it:
class myObj {
public:
typedef std::shared_ptr<myObj> handle;
typedef std::shared_ptr<const myObj> const_handle;
int someMethod() { ... }
int someConstMethod() const { ... }
};
Now what I need is a container class that somehow allows you to modify or read a collection of myObj depending on its own constness, like so:
class myCollection {
public:
typedef std::list<myObj::handle> objList;
typedef std::list<myObj::const_handle> const_objList;
inline objList& modify() { return _obl; }
// it would be nice to do this, but it won't compile as
// objList and const_objList are completely different types
inline const_objList& read() const { return _obl; } // doh! compile error...
// returning a const objList won't help either as it would return non-const
// handles, obviously.
// so I am forced to do this, which sucks as i have to create a new list and copy
void read(const_objList &l) {
std::for_each(
_obl.begin(),
_obl.end(),
[&l] (myObj::handle &h) { l.push_back(h); }
// ok as handle can be cast to const_handle
); // for_each
}
private:
objList _obl;
};
So this solution actually works as a const myCollection would only allow you to get a list of const_handle which only allows you to call non-modifying methods of myObj (GOOD).
The problem is that the "read" method is really ugly (BAD).
Another method would be to expose somehow the list methods and return const_handle and handle as needed but it's a lot of overhead, especially if you want to use something more complex than a list.
Any idea?
A list-of-pointers-to-T is not a list-of-pointers-to-constant-T.
std::list<std::shared_ptr<int>> a;
std::list<std::shared_ptr<const int>>& ra = a; // illegal but imagine it's not
std::shared_ptr<const int> x = std::make_shared<const int>(42);
ra.push_back(x); // totally legal, right?
++**a.begin(); // oops... just incremented a const int
Now a list-of-pointers-to-T is, conceptually, a constant-list-of-constant-pointers-to-constant-T, but std::list<std::shared_ptr<T>> does not support such a deep const propagation. const std::list<std::shared_ptr<T>> contains constant pointers to non-constant objects.
You can write your own variant of list<> or your own variant of shared_ptr<> that have such support. It probably won't be very easy though. A const_propagating_shared_ptr is probably the easier of the two. It would have to encapsulate an std::shared_ptr<T> object and forward almost everything to it as-is. As opposed to std::shared_ptr<T> it would have separate const and non-const versions of operator->, operator*() and get().
Given what you stated that you want to accomplish, I don't think that your solution is too bad. Imagine that some other code may be modifying the internal collection, like adding or removing values. Returning a copy of the current state of the collection is safe for client code, since it can work on the copy, without the danger of element being deleted in the meantime. But I digress, this is getting into threading issues and may not be relevant.
You could use prettier:
inline const_objList read() const
{
const_objList cl(_obl.begin(), _obl.end());
return cl;
}
However, I do think that your problems derive from mixing two types of constness: constness of the members of the collection versus the constness of the collection itself.
Instead of Modify and Read methods, that deal with the list as a whole, I would try exposing const and non-const iterators to internal list, through corresponding const and non-const methods returning said iterators.
But this immediately begs the question: why then have myCollection in the first place?
Creating entirely new collection type around std::list doesn't seem needed, unless you get a lot of proverbial bang for the buck from other, added functionality that is not visible in your sample.
You can then make your added functionality free methods that take std::list of your handles as the input. Not everything requires an object and operations on objects need not necessarily be member methods, unless access to private data is required.
You mentioned maybe using another container instead of the list. But your class, as is, won't do it, unless you have a template, where template parameter can be one of STL containers.
Which then implies that you should expose iterators.
Namely, if you foresee changing the internal collection type, you would want to make the public interface to myCollection transparent regarding the collection type. Otherwise, clients will have to recompile each time you change your mind about the internal implementation.
EDIT -----
Finally, if implementing iterators (while interesting and most correct) is too much, why not go for simple getters like in this SO post:
smart pointer const correctness
I'll quote the topmost answer by RĂ¼diger Stevens (it assumes vector instead of list):
template <typename T>
class MyExample
{
private:
vector<shared_ptr<T> > data;
public:
shared_ptr<const T> get(int idx) const
{
return data[idx];
}
shared_ptr<T> get(int idx)
{
return data[idx];
}
void add(shared_ptr<T> value)
{
data.push_back(value);
}
};
I'm working on an SDK which defines interfaces like
class FooIter
{
// Move to the next foo, return false if there is none.
virtual bool Move() = 0;
// Return a pointer to the current foo.
virtual const void* GetFoo() = 0;
// Get the size of a 'foo', which is a fixed-size POD.
virtual size_t GetFooSize() = 0;
// Get a comparator for foos.
virtual const FooComparator* GetComparator() = 0;
};
class FooComparator
{
virtual int compare(const void* first, const void* second) const = 0;
};
So basically, a foo is an opaque type that I can treat as a fixed-length binary buffer + and associated ordering function.
Now, I want to sort these foos before passing them back to client code. There may potentially be many foos, so I'll have to implement external sorting, but I'd like to use std::sort to sort the initial runs.
I was thinking I would allocate a buffer of size N * FooIter::GetFooSize(), fill it with foos using FooIter, and then sort it with std::sort before writing it to disk.
I can start by writing an iterator class
class FooBufferIter
{
public:
FooBufferIter(const void* fooAddr, int fooSize) : m_fooAddr(fooAddr), m_fooSize(fooSize) {}
FooWrapper operator*() {return FooWrapper(m_fooAddr, m_fooSize);}
FooBufferIter operator++() {return FooBufferIter(m_fooAddr + m_fooSize, m_fooSize);}
// All other needed iterator methods.
private:
const void* m_fooAddr;
int m_fooSize;
};
and a wrapper class for foo memory
class FooWrapper
{
public:
FooWrapper(const void* fooAddr, int fooSize) : m_fooAddr(fooAddr), m_fooSize(fooSize) {}
private:
const void* m_fooAddr;
int m_fooSize;
};
My understanding is that std::sort will use std::swap to rearrange the elements in the sequence. My issue is I can't see how to specialize std::swap on FooWrapper to perform the swap efficiently (most importantly, no dynamic allocation). I could swap byte by byte, but that also seems inefficient.
Another way of doing this would be to sort a parallel sequence of pointers into my Foo array, but I don't want to do that, since in practice, the foos will likely be quite small, so the parallel sequence could use just as much memory as the foo sequence, and I want to maximize the number of them I can sort at a time.
There's also good ol' qsort which is probably more suited to this kind of thing, but I'm not sure how to convert the FooComparator object into a function pointer (there may be multiple implementations of FooComparator).
Or is there a better way of going about this? I don't really want to write my own sort implementation, although it probably wouldn't be too hard.
I would build a buffer of void*, sort them, then produce the output buffer.
As a first step. Because easy. Then write everything else and look for performance bottlenecks.
As a next step, I would see if internal sorting with full type info can be done. Because optimal.
Failing that, a pod block pseudo reference iterator with a specialized swap. With tomfoolery for small med and big if performance tests justify further optimization so it sorts pointers for big and data for small.
But start with KISS and do the parts that have to be hard first.
I need to store a list of various properties of an object. Property consists of a name and data, which can be of any datatype.
I know I can make a class "Property", and extend it with different PropertySubClasses which only differ with the datatype they are storing, but it does not feel right.
class Property
{
Property(std::string name);
virtual ~Property();
std::string m_name;
};
class PropertyBoolean : Property
{
PropertyBoolean(std::string name, bool data);
bool m_data;
};
class PropertyFloat : Property
{
PropertyFloat(std::string name, float data);
float m_data;
};
class PropertyVector : Property
{
PropertyVector(std::string name, std::vector<float> data);
std::vector<float> m_data;
};
Now I can store all kinds of properties in a
std::vector<Property*>
and to get the data, I can cast the object to the subclass. Or I can make a pure virtual function to do something with the data inside the function without the need of casting.
Anyways, this does not feel right to create these different kind of subclasses which only differ by the data type they are storing. Is there any other convenient way to achieve similar behavior?
I do not have access to Boost.
C++ is a multi-paradigm language. It shines brightest and is most powerful where paradigms are mixed.
class Property
{
public:
Property(const std::string& name) //note: we don't lightly copy strings in C++
: m_name(name) {}
virtual ~Property() {}
private:
std::string m_name;
};
template< typename T >
class TypedProperty : public Property
{
public:
TypedProperty (const std::string& name, const T& data)
: Property(name), m_data(data);
private:
T m_data;
};
typedef std::vector< std::shared_ptr<Property> > property_list_type;
Edit: Why using std::shared_ptr<Property> instead of Property*?
Consider this code:
void f()
{
std::vector<Property*> my_property_list;
for(unsigned int u=0; u<10; ++u)
my_property_list.push_back(new Property(u));
use_property_list(my_property_list);
for(std::vector<Property*>::iterator it=my_property_list.begin();
it!=my_property_list.end(); ++it)
delete *it;
}
That for loop there attempts to cleanup, deleting all the properties in the vector, just before it goes out of scope and takes all the pointers with it.
Now, while this might seem fine for a novice, if you're an only mildly experienced C++ developer, that code should raise alarm bells as soon as you look at it.
The problem is that the call to use_property_list() might throw an exception. If so, the function f() will be left right away. In order to properly cleanup, the destructors for all automatic objects created in f() will be called. That is, my_property_list will be properly destroyed. std::vector's destructor will then nicely cleanup the data it holds. However, it holds pointers, and how should std::vector know whether these pointers are the last ones referencing their objects?
Since it doesn't know, it won't delete the objects, it will only destroy the pointers when it destroys its content, leaving you with objects on the heap that you don't have any pointers to anymore. This is what's called a "leak".
In order to avoid that, you would need to catch all exceptions, clean up the properties, and the rethrow the exception. But then, ten years from now, someone has to add a new feature to the 10MLoC application this has grown to, and, being in a hurry, adds code which leaves that function prematurely when some condition holds. The code is tested and it works and doesn't crash - only the server it's part of now leaks a few bytes an hour, making it crash due to being out of memory about once a week. Finding that makes for many hours of fine debugging.
Bottom line: Never manage resources manually, always wrap them in objects of a class designed to handle exactly one instance of such a resource. For dynamically allocated objects, those handles are called "smart pointer", and the most used one is shared_ptr.
A lower-level way is to use a union
class Property
union {
int int_data;
bool bool_data;
std::cstring* string_data;
};
enum { INT_PROP, BOOL_PROP, STRING_PROP } data_type;
// ... more smarts ...
};
Dunno why your other solution doesn't feel right, so I don't know if this way would feel better to you.
EDIT: Some more code to give an example of usage.
Property car = collection_of_properties.head();
if (car.data_type == Property::INT_PROP) {
printf("The integer property is %d\n", car.int_data);
} // etc.
I'd probably put that sort of logic into a method of the class where possible. You'd also have members such as this constructor to keep the data and type field in sync:
Property::Property(bool value) {
bool_data = value;
data_type = BOOL_PROP;
}
I suggest boost::variant or boost::any. [Related question]
Write a template class Property<T> that derives from Property with a data member of type T
Another possible solution is to write a intermediate class managing the pointers to Property classes:
class Bla {
private:
Property* mp
public:
explicit Bla(Property* p) : mp(p) { }
~Bla() { delete p; }
// The standard copy constructor
// and assignment operator
// aren't sufficient in this case:
// They would only copy the
// pointer mp (shallow copy)
Bla(const Bla* b) : mp(b.mp->clone()) { }
Bla& operator = (Bla b) { // copy'n'swap trick
swap(b);
return *this;
}
void swap(Bla& b) {
using std::swap; // #include <algorithm>
swap(mp, b.mp);
}
Property* operator -> () const {
return mp;
}
Property& operator * () const {
return *mp;
}
};
You have to add a virtual clone method to your classes returning a pointer to a newly created copy of itself:
class StringProperty : public Property {
// ...
public:
// ...
virtual Property* clone() { return new StringProperty(*this); }
// ...
};
Then you'll be able to do this:
std::vector<Bla> v;
v.push_back(Bla(new StringProperty("Name", "Jon Doe")));
// ...
std::vector<Bla>::const_iterator i = v.begin();
(*i)->some_virtual_method();
Leaving the scope of v means that all Blas will be destroyed freeing automatically the pointers they're holding. Due to its overloaded dereferencing and indirection operator the class Bla behaves like an ordinary pointer. In the last line *i returns a reference to a Bla object and using -> means the same as if it was a pointer to a Property object.
A possible drawback of this approach is that you always get a heap operation (a new and a delete) if the intermediate objects must be copied around. This happens for example if you exceed the vector's capacity and all intermediate objects must be copied to a new piece of memory.
In the new standard (i.e. c++0x) you'll be able to use the unique_ptr template: It
can be used inside the standard containers (in contrast to the auto_ptr which must not be used in the standard containers),
offers the usually faster move semantics (it can easily passed around) and
takes care over the held pointers (it frees them automatically).
I see that there are lots of shots at trying to solve your problem by now, but I have a feeling that you're looking in the wrong end - why do you actually want to do this in the first place? Is there some interesting functionality in the base class that you have omitted to specify?
The fact that you'd be forced to switch on a property type id to do what you want with a specific instance is a code smell, especially when the subclasses have absolutely nothing in common via the base class other than a name (which is the type id in this case).
Starting with C++ 17 we have something called as std::variant and std::any.
std::variant
An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value.
std::any
The class any describes a type-safe container for single values of any copy constructible type.
An object of class any stores an instance of any type that satisfies the constructor requirements or is empty, and this is referred to as the state of the class any object. The stored instance is called the contained object. Two states are equivalent if they are either both empty or if both are not empty and if the contained objects are equivalent.
The non-member any_cast functions provide type-safe access to the contained object.
You can probably do this with the Boost library, or you could create a class with a type code and a void pointer to the data, but it would mean giving up some of the type safety of C++. In other words, if you have a property "foo", whose value is an integer, and give it a string value instead, the compiler will not find the error for you.
I would recommend revisiting your design, and re-evaluating whether or not you really need so much flexibility. Do you really need to be able to handle properties of any type? If you can narrow it down to just a few types, you may be able to come up with a solution using inheritance or templates, without having to "fight the language".
I wrote a little "lazy vector" class (or, delayed vector) which is supposed to look like a std::vector and usable wherever a std::vector is used, but it loads its elements "lazily", i.e. it will load element n (and possibly a few more) from disk whenever someone accesses element n. (The reason is that in my app, not all elements fit into memory.)
Here is this LazyVector class, but there is a problem with const member functions that use such a vector, see below.
template<class T>
class LazyVector {
std::vector<T> elems_;
void fetchElem(unsigned n){
// load the n-th elem from disk into elems_ etc
}
public:
const T& operator[](unsigned n) const {
fetchElem(n); // ERROR: ... discards qualifiers
return elems_[n];
}
T& operator[](unsigned n) {
fetchElem(n);
return elems_[n];
}
// and provide some other std::vector functions
};
As I said, there is a problem when a const member function asks for an element of the LazyVector. By nature of the LazyVector, accessing an element is not const, i.e. it will change the vector vec below, which is forbidden in this context. The foo member function must be const and cannot be changed. How can I solve this?
class Foo {
LazyVector<const std::string*> vec;
void fct(int n) const { // fct must be const
const std::string* str = vec[n];
// do something with str
}
};
You can either use mutable member data or const_cast in the implementation of your LazyVector class. Thus you can create the illusion of constness needed by your consuming class without actually being const.
Use the mutable keyword on the elems_ data member.
The const operator is used to show that the object is logically const.
The fact that your data is on disk is neither here nor there your object is not changing state so you can delegate the work for actually holding the data to another object a cache (Where the data is stored is an implementation details and not part of the objects state).
class LazyVector
{
public:
int const& operator[](int index) const
{
data->fetchElement(index);
return data->get(index);
}
private:
std::auto_ptr<LazyDataCache> data;
};
Here data is a pointer (a smart pointer but still a pointer). As long as the pointer does not change you are not changing the cost-ness of the LazyVector. But you can still call non const methods on the object pointed at by data (remember it is the pointer that is const NOT the object pointed at).
For such things, the mutable keyword is for. Put your cache as a mutable object into your class. That is because your cache seems to not change the logical content/state of your object (i.e the elements of your vector or the size of it do not change).
const methods do not state they don't physically change your object. They state that they won't change the abstract value of your object. Implementation details that are abstracted away may still be changed by const functions.
The mutable is for this kind of cases. Make your vector mutable or add a mutable cache member that contains some sort of cache entries.
Read the What are the semantics of a const member function answer by Anthony Williams.
Declare elems_ as mutable:
mutable std::vector<T> elems_;
There's other stuff you can do, but that's the supported way of doing it.
Edit: Another way of doing this is to add another member and set it in the constructor:
std::vector<T> *mutable_elems_;
mutable_elems_(&elems_)
A crude way of doing this would be
LazyVector* p = const_cast<LazyVector*>(this);
p->fetch();
I guess there will be better way of doing this. But this will work.