In C++ I'm often facing a situation when I need to prepare const and non-const version of class in analogy to const_iterator and iterator from standard library.
class const_MyClass
{
public:
const_MyClass(const int * arr):
m_arr(arr)
{
}
int method() const; //does something with m_arr without modifying it
private:
const int * m_arr;
}
class MyClass
{
public:
MyClass(int * arr):
m_arr(arr)
{
}
int method() const; //does something with m_arr without modifying it
void modify(int i); //modify m_arr
private:
int * m_arr;
}
The problem with this is that I need to repeat whole code of const_MyClass in MyClass and distribute any changes in API to both classes. Thus sometimes I inherit const_MyClass and do some const_casts, which also isn't perfect and pretty solution. Still when I want to pass const_MyClass instance by reference it looks moronic:
void func(const const_MyClass & param)
Instance param is marked with two "consts", and it has only const methods...
This is where const constructors would be handy, but are there any existing alternatives?
Some use examples to explain problem better:
//ok to modify data
void f(int * data)
{
MyClass my(data);
my.modify();
...
}
//cant modify data, cant use MyClass
void fc(const int * data)
{
const_MyClass my(data);
int i = my.method();
...
}
You can make a template class to act as a base, like this:
template<typename T>
class basic_MyClass
{
public:
basic_MyClass(T * arr) :m_arr(arr) {}
int method() const; //does something with m_arr without modifying it
private:
T * m_arr;
};
Then, for your const version, since it doesn't add anything, you can just use a typedef:
typedef basic_MyClass<const int> const_MyClass;
For your non-const version, you can inherit:
class MyClass : public basic_MyClass<int>
{
public:
using basic_MyClass::basic_MyClass; // inherit all the constructors
void modify(int i); //modify m_arr
};
Have you considered simply tracking two pointers and raising exceptions from the mutable operations when no mutable value is available? Maybe an example will help describe what I am thinking of.
class MyClass
{
public:
MyClass(int *mutable_data):
m_mutable_view(mutable_data), m_readonly_view(mutable_data)
{
}
MyClass(const int *immutable_data):
m_mutable_view(NULL), m_readonly_view(immutable_data)
{
}
int retrieve_value(int index) {
return m_readonly_view[index];
}
void set_value(int index, int value) {
require_mutable();
m_mutable_view[index] = value;
}
protected:
void require_mutable() {
throw std::runtime_error("immutable view not available");
}
private:
const int *m_readonly_view;
int *m_mutable_view;
};
The idea is pretty simple here - use a sentinel value to indicate whether modifications are possible or not instead of depending on the type system to do that for you. Personally, I would think about doing the inheritance based approach that #BenjaminLindley suggested but I wanted to present a slightly different solution that might not have occurred to you.
After talk with Neil Kirk I realized what I was doing wrong. I started by separating data from logic as he suggested.
This attempt resulted in two classes MyClassPtr and const_MyClassPtr. They only provide functions for data access (like iterators) and may look like that:
class const_MyClassPtr
{
public:
const_MyClassPtr(const int * arr);
int operator [](int i) const;
const int * ptr() const;
private:
const int * m_arr;
}
class MyClassPtr
{
public:
MyClassPtr(int * arr);
int operator [](int i) const;
int & operator [](int i);
const int * ptr() const;
int * ptr();
//promotion to const pointer
const_MyClassPtr () const {return const_MyClassPtr(m_arr);}
private:
int * m_arr;
}
Now it is clear that objects of these classes should be treated like pointers, so when I use them as function parameters I pass them by value!
void func(const_MyClassPtr param) //instead of void func(const const_MyClass & param)
To provide methods I have created MyClassOp class template and used static polymorphism.
template <class DERIVED>
class MyClassOp
{
public:
const DERIVED & derived() const {return static_cast<const DERIVED &>(*this)}
DERIVED & derived() {return static_cast<DERIVED &>(*this)}
int method() const; //operates on derived() const
void modify(int i); //operates on derived()
}
MyClassOp is a collection of methods. It does not have state. In general it is a trait. To make these methods accessible I overloaded -> and * operators
class const_MyClassPtr : private MyClassOp<const_MyClassPtr>
{
public:
const MyClassOp<MyClassPtr> * operator ->() const {return this;}
const MyClassOp<MyClassPtr> & operator *() const {return *this;}
...
}
class MyClassPtr : private MyClassOp<MyClassPtr>
{
public:
MyClassOp<MyClassPtr> * operator ->() {return this;}
MyClassOp<MyClassPtr> & operator *() {return *this;}
...
}
This works O.K., but is a bit cumbersome. If I have for example equality operator I need to write something like *myptr1 == myptr2 to compare values kept by two MyClassPtr objects (it's easy to make a mistake and compare myptr1 == myptr2 or expect that something like *myptr1 == *myptr2 could work). Also when I have allocating type:
class MyClass : public MyClassOp<MyClass>
{
MyClass(int x, int y, int z);
...
int m_arr[3];
}
I would want to be able to use temporaries as function arguments.
void f(const_MyClassPtr my);
//use temporary when calling f()
f(MyClass(1, 2, 3));
I can do this by providing conversion operators or conversion constructors (that convert MyClass to const_MyClassPtr). But then const_MyClassPtr behaves more like reference than pointer. If iterators are generalization of pointers then why one could not imitate reference? Therefore I divided MyClassOp into two parts (const and non const) and replaced -> and * operators implemented by const_MyClassPtr and MyClassPtr with public inheritance and changed their names to ressemble reference. I ended up with following structures.
MyClassOp : public const_MyClassOp
const_MyClassRef : public const_MyClassOp<const_MyClassRef>
MyClassRef : public MyClassOp<MyClassRef>
MyClass : public MyClassOp<MyClass>
However const_MyClassRef and MyClassRef are not perfect generalization of reference as it impossible to imitate some of C++ reference properties, so Ref suffix is there to denote reference-like structure.
Maybe you can find some hints in effective c++ item 4 "Avoid duplication in const and non-const Member function"
I may summarize like following ( it makes you avoid code duplication even if using somewhat ugly cast ):
struct my_class
{
my_class(int x):_x(x){};
const int& method(void) const;
int& method(void);
int _x;
};
const int& my_class::method(void) const //func for const instance
{
return _x;
}
int& my_class::method(void) //func for normal instance
{
return const_cast<int& >(static_cast<const my_class& >(*this).method()) ;
}
int main()
{
my_class a(1);
const my_class b(2);
a.method() = 5;
cout << a.method() << endl;
//b.method() = 4; //b is const, wont compile
cout << b.method() << endl;
return 0;
}
Related
I am working on a plugin that runs inside a host program against a proprietary PDK. At times there will be breaking changes in the PDK, so my code uses wrapper classes that allow it to work with more than one version of the host while encapsulating the changes from version to version.
Here is a very simplified example that illustrates the kind of issue I would like to address. Of course, I'm dealing with many more members that 2.
struct DataV1 // I cannot modify this
{
int a;
float b;
};
struct DataV2 // I cannot modify this
{
float b;
int a;
long c;
};
class DataWrapper // my class
{
private:
bool _forV1; // determined at run-time
DataV1 _dataV1;
DataV2 _dataV2;
public:
DataWrapper(); // initializes _forV1
int GetA() const;
void SetA(int value);
float GetB() const;
void SetB(float value);
long GetC() const { return _dataV2.c } // only exists in v2
void SetC(long value) { _dataV2.c = value; } // only exists in v2
};
I would like to avoid duplicating in every getter and setter the logic that chooses the member from one version of the struct or the other. Note that while the order of members is rearranged, the types and member names are the same. I came up with this macro:
#define DATA_ACCESS(MEMBER) const_cast<decltype(_dataV1.MEMBER)&>(([&]() -> const decltype(_dataV1.MEMBER)& \
{ return (_forV1) ? _dataV1.MEMBER : _dataV2.MEMBER; })())
This allows for a somewhat elegant implementation of the property accessor functons:
int GetA() const { return DATA_ACCESS(a); }
void SetA(int value) { DATA_ACCESS(a) = value; }
float GetB() const { return DATA_ACCESS(b); }
void SetB(float value) { DATA_ACCESS(b) = value; }
I am posting this question to see if anyone has a better idea, especially an idea that doesn't involve a macro. Thanks.
With std::variant, you might do something like:
class DataWrapper // my class
{
private:
std::variant<DataV1, DataV2> data;
public:
DataWrapper(); // initializes _forV1
int GetA() const { return std::visit([](auto& arg){ return arg.a; }, data); }
void SetA(int a) const { std::visit([&a](auto& arg){ arg.a = a; }, data); }
// ...
};
I am making a class that shares data between several objects using dynamic memory. The relevant parts of the class are shown below.
class StrBlob
{
public:
StrBlob::StrBlob(std::initializer_list<std::string> il) :
data(std::make_shared<std::vector<std::string>>(il)) {}
void push_back(const std::string &t) const { data->push_back(t); }
private:
std::shared_ptr<std::vector<std::string>> dataPtr;
};
I understand that by making push_back a const member function, I am saying that this function will not change the member dataPtr. However, the underlying vector that dataPtr points to is not const, as shown by the below code.
//The result is foo = {"bar", "foobar"}
const StrBlob foo = {"bar"};
foo.push_back("foobar");
Is it possible or even desirable to make the underlying vector const by making the StrBlob object const? Should be something like making dataPtr a pointer to const when the object is const, but I'm not sure how to achieve that.
You can make a thin wrapper around std::shared_ptr (details omitted like ctor etc):
#include <memory>
template< class T > class const_propagated_shared_ptr {
std::shared_ptr<T> m_ptr;
public:
T &operator*() { return m_ptr.operator*(); }
T* operator->() { return m_ptr.operator->(); }
const T &operator*() const { return m_ptr.operator*(); }
const T *operator->() const { return m_ptr.operator->(); }
};
class Foobar {
const_propagated_shared_ptr<int> m_ptr;
public:
void f1() { *m_ptr = 10; }
void f2() const { *m_ptr = 10; } // compile error
};
But as this is implementation detail of the class (Foobar in this case) I am not sure that it worse the effort, as to let modify data by const method or not can be controlled by class designer.
I have a class that can generically hold any (primitive) type:
class Value
{
private:
int i_value;
unsigned int ui_value;
long l_value;
unsigned long ul_value;
short s_value;
float f_value;
double d_value;
char c_value;
bool b_value;
std::string str_value;
int type;
void setValue(int value);
void setValue(unsigned int value);
void setValue(long value);
void setValue(unsigned long value);
void setValue(short value);
void setValue(float value);
void setValue(double value);
void setValue(char value);
void setValue(bool value);
void setValue(std::string value);
public:
Value(int value);
Value(unsigned int value);
Value(long value);
Value(unsigned long value);
Value(short value);
Value(float value);
Value(double value);
Value(char value);
Value(bool value);
Value(std::string value);
Value(Value& other); //Copy Constructor
~Value();
int getType();
std::string toString(int format);
};
This is nice, because I can do something like:
Value * v1 = new Value(55);
Value * v2 = new Value(1.2);
Value * v3 = new Value("yes");
Value * v4 = new Value(true);
However, as you can see, it's pretty ugly; tons of overloading of everything to make it work. I was thinking templates could make this generic. However, as far as I can tell, you always have to specify the type, which sort of defeats the whole purpose of the class.
For example:
Value<int> * v1 = new Value<int>(55);
Value<double> * v2 = new Value<double>(1.2);
Value<string> * v3 = new Value<string>("yes");
Value<bool> * v4 = new Value<bool>(true);
If I use templates, I can no longer do something like vector<Value *> like I could before. Is this correct, or am I missing some aspect of templates that could help in this situation?
All you need is a parent base class for the template one:
class BaseValue
{
public:
virtual ~BaseValue()
{}
};
template<typename T>
class Value : public BaseValue
{
public:
Value(const T& value)
:m_value(value)
{}
void set(const T& value)
{
m_value = value;
}
const T& get()
{
return m_value;
}
virtual ~Value()
{}
private:
T m_value;
};
std::vector<BaseValue*> values;
values.push_back(new Value<int>(1)); // int
values.push_back(new Value<double>(1.0)); // double
values.push_back(new Value<char*>("asdf")); // pointer to array on stack :(
values.push_back(new Value<char>('c')); // char
First question: Use a template factory function instead of new.
Second question: Use a common base-class.
In order to be able to properly delete the objects pointed to by the pointers on the vector, you need a virtual destructor. Also, in order to do anything useful with the pointers to the base class, you need virtual methods in the base class.
Example:
class ValueBase
{
public:
virtual ~ValueBase() = default;
virtual void Print(std::ostream & os) const = 0;
};
std::ostream & operator<< (std::ostream & os, const ValueBase & value)
{
value.Print(os);
return os;
}
template<typename T> class Value : public ValueBase
{
T value;
public:
Value(const T & v) : value(v) {}
const T & Get() const;
void Set(const T & v);
void Print(std::ostream & os) const
{
os << value;
}
// ...
};
template<typename T> Value<T> * NewValue(const T & v)
{
return new Value<T>(v);
}
Now you can do
ValueBase * v1 = NewValue(55);
ValueBase * v2 = NewValue(1.2);
ValueBase * v3 = NewValue<std::string>("yes");
ValueBase * v4 = NewValue(true);
std::vector<ValueBase *> vec;
vec.push_back(v1);
vec.push_back(v2);
vec.push_back(v3);
vec.push_back(v4);
vec.push_back(NewValue(2350983444325345ll));
for (const auto & entry : vec)
{
std::cout << *entry << " ";
}
std::cout << "\n";
Note that you normally do not need the template argument of NewValue, since it will be deduced. With "yes" this does not work, since Value<T> will be instantiated with T = char [4] which would require you to use strcpy in the constructor. I find it quite nice to explicity state that a conversion is needed as above. If you prefer this implicitly, do an overload:
Value<std::string> * NewValue(const char * v)
{
return new Value<std::string>(v);
}
Make sure to delete the contents manually
for (const auto & entry : vec)
{
delete entry;
}
vec.clear();
or use std::unique_ptr instead of the bare pointer:
template<typename T> std::unique_ptr<Value<T>> UniqueValue(const T & v)
{
return std::unique_ptr<Value<T>>(new Value<T>(v));
}
std::vector<std::unique_ptr<ValueBase>> vec;
vec.push_back(NewValue(4.5));
If you extend Value and it turns out you need to do something in the destructor, you have to implement the copy constructor, assignment operator and possibly also the move constructor and move assignment operator. ("Rule of Three" or "Rule of Five".) In the above version the "Rule of Zero" still applies, since the destructor is still the same as the implicitly defined one (= default).
If you add the destructor it should be virtual. Otherwise you might get memory leaks if you delete the VirtualBase pointers.
Of course you can do templates, but you also need to do polymorphic.
class StupidAndEmpty {} // add virtual destructor if Value needs a destructor
template<class dType>
Value : StupidAndEmpty {
// do smart things with dType
}
vector<StupidAndEmpty *> notSoStupid;
Only problem is how do you use them when you get them back from vector.
I have the following (very simplified) "container" class:
class container
{
public:
template<typename T> container(const boost::shared_ptr<T> &rhs)
: m_content(rhs) { }
template<typename T>
operator T const & () const
{
return get<T>();
}
template<typename T>
T const & get() const
{
return *boost::any_cast< boost::shared_ptr<T> >(m_content);
}
private:
boost::any m_content;
};
It should store objects in the boost::any container in the form of a shared pointer. If I store some object, say, of the boost::shared_ptr<some_type> type in the container, I would like to get the reference (const some_type&) simply by a user-defined conversion which would allow to do something like this:
boost::shared_ptr<some_type> x(new some_type);
container cx = x;
...
// user-defined conversion
const some_type &y = cx;
// a template conversion using a "getter"
const some_type &y = cx.get<some_type>();
Sometimes, I need to store objects derived from some abstract type and do the same sort of type conversion to the reference of this abstract type, for example, like this:
boost::shared_ptr<some_abstract_type> x(new some_derived_type);
container cx = x;
...
// user-defined conversion
const some_abstract_type &y = cx;
// a template conversion using a "getter"
const some_abstract_type &y = cx.get<some_abstract_type>();
Both the user-defined conversion and the template "getter" work fine with GCC. However, the Intel C++ compiler seems to have a problem with the (user-defined) conversion while the "getter" works.
For example, the following code works with GCC but not with Intel:
#include <iostream>
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
class container
{
public:
template<typename T> container(const boost::shared_ptr<T> &rhs)
: m_content(rhs) { }
template<typename T>
operator T const & () const
{
return get<T>();
}
template<typename T>
T const & get() const
{
return *boost::any_cast< boost::shared_ptr<T> >(m_content);
}
private:
boost::any m_content;
};
class base
{
public:
virtual ~base() { }
virtual void f() const = 0;
};
class derived : public base
{
public:
virtual ~derived() { }
virtual void f() const { std::cout << "hello\n"; }
};
void foo(const container &c)
{
const base & a = c;
a.f();
}
int main()
{
boost::shared_ptr<base> a(new derived);
container c = a;
foo(c);
}
With Intel, I get this error:
test.cpp(44): error: no suitable user-defined conversion from "const container" to "const base" exists
const base & a = c;
^
compilation aborted for test.cpp (code 2)
On the other hand, if I replace base with derived in both main() and foo() (or use the "getter" instead of the type conversion in foo()), everything works fine with Intel too. Is it possible to convince the Intel compiler to use the user-defined type conversion to the reference type when T is an abstract class?
Thanks in advance for any ideas.
EDIT: Interestingly, using the type conversion to the pointer type works fine. If I add
template<typename T>
operator T const * () const
{
return &get<T>();
}
to the container class and replace foo() with
void foo(const container &c)
{
const base * a = c;
a->f();
}
then it works also with Intel.
I would return a pointer in the getter:
template<typename T>
T const * get() const {
return boost::any_cast< boost::shared_ptr<T> >(m_content);
}
This avoids the conversion problem, and does not crash immediately if you pass a null pointer to your container.
Example:
void foo(const container &c)
{
const base* a = c.get<base>();
a->f();
}
You could also add a function valid() which checks if there is something in the container:
bool valid() const {
return m_content != NULL;
}
Edit: Your addition to your question follows exactly in this direction.
Ok, so it seems that it is a bug in the Intel C++ compiler and was filed in the bug tracking list.
I'm stuck with this piece of code:
class MyObject
{
public:
int value;
}
class MyClass
{
private:
btAlignedObjectArray<MyObject*> m_objects;
public:
int comp (MyObject *a, MyObject *b)
{
return calculateTheNewValue(a->value) < calculateTheNewValue(b->value);
}
void doSort()
{
m_objects.quickSort(comp);
}
//edit: this member function is needed to do the sorting
int calculateTheNewValue(int v)
{
// do some calculation using other members variables, not necessarily m_objects
}
};
It doesn't compile because comp is a non static member function.
comp cant be static, because it needs to access the member variable m_objects.
Also it would defeat the encapsulation of m_objects to have a static function and call it like this
MyClass::doSort(myClass.m_objects)
Edit
This is the declaration of btAlignedObjectArray
http://bulletphysics.org/Bullet/BulletFull/btAlignedObjectArray_8h_source.html
Line 365 has the declaration or quicksort
If you need to make comp into a binary function, then wrap it in a functor. If you can use C++11, then use a lambda:
m_objects.quickSort([&](MyObject * lhs, MyObject * rhs) {
return this->comp(lhs,rhs)
});
If you can't use C++11, then make a functor class with similar behavior.
struct compare
{
MyObject & obj_;
compare(MyObject& obj) :obj_(obj) {}
bool operator()(MyObject * lhs, MyObject * rhs) const {
return obj_.comp(lhs,rhs);
}
};
...
void doSort()
{
m_objects.quicksort(compare(*this));
}