I have a class template which looked like this:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(some_mutex);
return some_policy.some_operation(some_data);
}
private:
T some_data;
mutable Mutex some_mutex;
SomePolicy some_policy;
};
If not used concurrently, we have a dummy mutex type which has all the member functions as inlined empty functions and no data. There are policies that have per-instance data and those that do not have any data.
This is library code and it turns out that this class template gets used in application code where the extra bytes matter which are needed for the data members some_mutex and some_policy even when they are empty classes. So I want to make use of the empty base optimization. For the policy, this is easy:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(the_data.some_mutex);
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy {
T some_data;
mutable Mutex some_mutex;
};
data the_data;
};
However, given that some_mutex is mutable, I don't know how to make it a base class without making the_data, and thus all data, mutable, thereby completely taking over the compiler's responsibility to protect me from silly constness mistakes.
Is there a way to make a turn a mutable data member into a base of a non-mutable data member's class?
No, a base class cannot be mutable. But...
thereby completely taking over the compiler's responsibility to protect me from silly constness mistakes.
...that's not necessarily the result of that. You can still let the compiler help you, by creating accessor functions instead of using your data structure directly. And you can name it in such a way that it should be obvious to everyone that those accessor functions are the only supported interface to the data.
mutable struct : SomePolicy, Mutex {
T some_data;
} _dont_use_directly;
T &some_data() { return _dont_use_directly.some_data; }
const T &some_data() const { return _dont_use_directly.some_data; }
SomePolicy &some_policy() { return _dont_use_directly; }
const SomePolicy &some_policy() const { return _dont_use_directly; }
Mutex &some_mutex() const { return _dont_use_directly; }
What you could do is use a mutex wrapper and specialize it for the empty mutex for which you then can perform the EBCO.
class EmptyMutex{
void lock() const {};
void unlock() const {};
};
template< class MUX>
class MutexWrapper {
mutable MUX mux;
public:
void lock() const {mux.lock();};
void unlock() const { mux.unlock() ;};
};
template<>
class MutexWrapper<EmptyMutex> : public EmptyMutex {};
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(the_data);
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy ,MutexWrapper<Mutex> {
T some_data;
};
data the_data;
};
The caveat of this solution is, that - inside a const memberfunction - while you can use the lock() and unlock() functions directly, you can only pass const references to the MutexWrapper as parameters.
So in this case, your resource_lock would have to take a const reference to a MutexWrapper - when one would expect (and rightly so) that it actually changes the state of the mutex. This is quite missleading for someone, who doesn't know how MutexWrapper is implemented.
For that reason I think it is more sensible to just const_cast the mutex when needed instead of using a wrapper:
template<typename T, typename Mutex, typename SomePolicy>
class my_class {
public:
T f() const {
resource_lock a_lock(getNonConstMuxRef());
return the_data.some_operation(the_data.some_data);
}
private:
struct data : SomePolicy, Mutex {
T some_data;
};
data the_data;
Mutex& getNonConstMuxRef() const { return const_cast<my_class<T, Mutex, SomePolicy>*>(this)->the_data; }
};
Assuming your std::tuple implements the empty base optimization (do check), then this might help:
mutable std::tuple<T, Mutex, SomePolicy> raw;
T const& data() const { return std::get<0>(raw); }
T & data() { return std::get<0>(raw); }
Mutex & mutex() const { return std::get<1>(raw); }
SomePolicy const& policy() const { return std::get<2>(raw); }
SomePolicy & policy() { return std::get<2>(raw); }
basically we put the optimization into a .raw mutable member that we never otherwise access (as a bonus, tuple's access is messy). Then we create reference-accessors that enforce const.
You may also want to:
my_class(my_class const& )=default;
my_class(my_class && )=default;
my_class&operator=(my_class const& )=default;
my_class&operator=(my_class && )=default;
to be explicit that my_class const&& is not in play. This also assumes that T and other types have well-behaved copy ctors and the like. (as in, they don't have a T(T&) ctor or operator= that feels overly entitled about the non-const-ness of the rhs)
Related
Shared pointer to an immutable type has value semantics
I am trying to create a builder class which has value semantics which looks like something like this
class Pipeline {
public:
Pipeline(
const std::string& name,
std::optional<size_t> limitIn,
std::optional<size_t> limitOut) {...}
shared_ptr<const Pipeline> limitInput(size limit) const {
return make_shared<Pipeline>(name_, size_, limit, limitOut_) ;
}
shared_ptr<const Pipeline> limitOutput(size limit) const {
return make_shared<Pipeline>(name_, size_, limitInput_, limit) ;
}
private:
const string name_;
const size_t limitInput_;
const size_t limitOutput_;
};
Since member variables are const, shared_ptr is essentially immutable.
However this pattern breaks down when I need to add inheritance. For ex:
class Pipeline {
...
virtual void doSomething() const = 0;
}
Now in any of the methods (say in limitOutput) when I need to create a new instance of Pipeline I would need to know about the derived class as well since I cannot create an instance of Pipeline anymore. One way I can think of to solve this is to add another virtual method to initialize the object.
class Pipeline {
...
virtual shared_ptr<Pipeline> create(const std::string& name,
std::optional<size_t> limitIn,
std::optional<size_t> limitOut) const = 0;
}
class SpecialPipeline : public Pipeline {
...
virtual shared_ptr<Pipeline> create(const std::string& name,
std::optional<size_t> limitIn,
std::optional<size_t> limitOut) const override {
return make_shared<SpecialPipeline>(name, limitIn, limitOut);
}
};
Now all methods would just defer to this
shared_ptr<const Pipeline> limitInput(size limit) const {
return create(name_, size_, limit, limitOut_);
}
While this works I personally feel it isn't elegant, involves duplication construction and doesn't feel idiomatic. How would one go about implementing this ? Any feedback would be appreciated.
The simplest way to deal with this problem is:
Don't expose the constructor; make it protected.
Provide factory members on the base type and derived types that return std::unique_ptr<const T> or std::shared_ptr<const T>.
Remove the const qualification on your data members.
Add a virtual clone method that makes a copy and returns std::unique_ptr<Pipeline>.
From the outside, there is no way to obtain a non-const object since the constructor is not public, so the members do not need to be const.
Since the members are not const, your mutation-factory methods can:
Invoke the clone method to make a copy.
Mutate the data member on the copy.
Extract the pointer from the std::unique_ptr and return a new smart pointer with a const target.
I would use the Curiously Recurring Template Pattern here:
template <class T>
class Pipeline {
public:
Pipeline(
const std::string& name,
std::optional<size_t> limitIn,
std::optional<size_t> limitOut) {...}
shared_ptr<const T> limitInput(size limit) const {
return make_shared<T>(name_, limit, limitOut_) ;
}
shared_ptr<const T> limitOutput(size limit) const {
return make_shared<T>(name_, limitInput_, limit) ;
}
...
};
And in child classes:
class Child: public Pipeline<Child> {
public:
Child(
const std::string& name,
std::optional<size_t> limitIn,
std::optional<size_t> limitOut): Pipeline<Child>(name, limitIn, limitOut) {}
...
];
That way the child classes have only to have a constructor with the same parameters as their parent and delegate to this one.
Currently, I am building a library in C++ (using C++11 standards), and I am stuck on trying to figure out how to make my design more practical. I have the following abstract class E
template<typename K>
class E
{
public:
virtual ~E() {};
virtual void init() = 0;
virtual void insert(const K& k) = 0;
virtual size_t count() const = 0;
virtual void copy(const E<Key>& x) = 0;
};
which I want to restrict users from instantiating it (i.e., be an interface). E has two sub-classes that implement the corresponding methods:
template<typename K>
class EOne : public E<K>
{
public:
EOne() {}
EOne(const EOne& x) {...}
void init() override
{
...
}
void insert(const K& v) override
{
...
}
size_t count() const override
{
...
}
void copy(const E<K>& o) override
{
...
}
private:
// some private fields
};
and ETwo : public E<K>, which is similar to EOne. Also, there is a different class J, which has a member std::vector<E<K>> that needs to be instantiated during construction:
template<typename K>
class J
{
public:
J(size_t n, const E<K>& e) : v(n, e)
{}
private:
std::vector<E<K>> v;
}
In essence, by getting a constant reference for a E<K> object, I want J's constructor to use the reference to instantiate all n objects of v using e as a template (i.e., call the copy constructor). As you can imagine, my goal is to have e be an object of either EOne<K> or ETwo<K>. For instance, I would make a call to J<K>::J(size_t n, const E<K>& e) the following way:
int main(int argc, char** argv)
{
EOne<std::string> e;
J<std::string> j(10, e); // populate v with 10 copies of e
...
}
However, the above does not compile and the compiler complains that I cannot instantiate abstract class (I am using vc++ but I believe I will get the same error on other compilers as well). Therefore, my question has to do on how I can overcome this problem? Do you have any suggestions on how I can make my design more practical.
Thank you
There is more than one approach to this. What follows is the most complex reasonable one. It requires lots of work in the type definitions, but leads to the cleanest "client" code that uses these types.
It is time to learn how to make a type regular.
An instance of a regular type behaves like a value. C++ algorithms and container work far better with regular types than it does with abstract types.
template<class K>
class E_interface {
public:
virtual ~E_interface() {};
virtual void init() = 0;
virtual void insert(const K& k) = 0;
virtual size_t count() const = 0;
virtual void copy_from(const E_interface& x) = 0;
std::unique_ptr<E_interface> clone() const = 0;
};
this is basically your E, except I added clone().
template<class T, class D=std::default_delete<D>, class Base=std::unique_ptr<T>>
struct clone_ptr:Base {
using Base::Base;
clone_ptr(Base&& b):Base(std::move(b)) {}
clone_ptr()=default;
clone_ptr(clone_ptr&&o)=default;
clone_ptr(clone_ptr const& o):
clone_ptr(
o?clone_ptr(o->clone()):clone_ptr()
)
{}
clone_ptr& operator=(clone_ptr&&o)=default;
clone_ptr& operator=(clone_ptr const&o) {
if (*this && o) {
get()->copy_from(*o.get());
} else {
clone_ptr tmp(o);
*this = std::move(tmp);
}
return *this;
}
};
The clone_ptr is a smart pointer that is a unique_ptr that knows how to copy itself by calling clone() and copy_from on the stored object. It may have some typos.
Now we write our E:
template<class K>
class E {
clone_ptr<E_interface<K>> pImpl;
public:
E() = default;
E( std::unique_ptr<E_interface<K>> in ):pImpl(std::move(in)) {}
E( E const& )=default;
E( E && )=default;
E& operator=( E const& )=default;
E& operator=( E && )=default;
explicit operator bool() const { return (bool)pImpl; }
void init() { if (*this) pImpl->init(); }
void insert(const K& k) ( if (*this) pImpl->insert(k); }
size_t count() const { if (*this) pImpl->count(); else return 0; }
};
Our E<K> is now a value type. It can be stored in a vector, copied, moved, etc.
How do we do EOne and ETwo?
First, take your existing EOne and ETwo and call them EOne_impl and ETwo_impl. Implement a clone() function that does a return std::make_unique<EOne_impl>(*this); for EOne_impl and similar for ETwo_impl.
Then this helper:
template<class Impl, class K>
struct E_impl: E<K> {
using E<K>::E<K>;
E_impl() : E<K>( std::make_unique<Impl>() ) {}
template<class...Args>
E_impl(Args&&...args) : E<K>( std::make_unique<Impl>(std::forward<Args>(args)...) ) {}
};
gives us
template<class K>
using Eone = E_impl< Eone_impl, K >;
template<class K>
using Etwo = E_impl< Etwo_impl, K >;
and I believe your J and main code starts compiling and working as-is.
What we just did was create value-semantics E<K> type that contains a pImpl (pointer to implementation) pointing to a pure-virtual interface that knows how to copy itself, as well as the interface we want on an E<K>.
We then forwarded the interface of E<K> to the E_interface<K> for each method. We didn't expose copy_from or clone, as those become operator= and our copy constructor.
To implement E<K>, you first implement E_interface<K>. Then I wrote a helper to create a derived type from E<K> that implicitly uses that implementation.
Note that our E<K> is almost-never empty; not never-empty. This is more efficient and simpler, but can cause problems down the road.
E<K> becomes a polymorphic value-semantics type. This is a strange beast in some senses (as many languages don't support such a type), but in other senses it behaves exactly the way you'd want it to behave.
A similar solution in C# or Java would have the data stored in the vectors be fully garbage collected reference-semantics types, not value-semantics types.
This is similar to a std::vector<std::shared_ptr<E<K>> (with the note that shared pointers are not fully garbage collected). Also note that copies of the shared_ptr end up being pointing to the same object, not new copies of it.
A std::vector<value_ptr<E_interface<K>> would also be a reasonable solution and get rid of some of the gymnastics I did in my E<K> and E_impl<K>. In this case, you wouldn't rename E to E_interface. You'd initialize the vector with
J<std::string> j(10, std::make_unique<EOne<std::string>>(e))
or somesuch.
Part of your problem is you have to ask yourself "what does it mean to copy an E<K>". In C++ you get to answer this question yourself; depending on how you answer it, you may or may not be permitted to store it in a std::vector.
Since std::vector<E<K>> v; requires a static instantiation of class E, it will never compile (as you already noticed correctly). To make it work should use
std::vector<std::shared_ptr<E<K>>> v;
instead. It can store your objects EOne and ETwo dynamically while they can be referred with a pointed of type E. To add a new object to the vector you can use push_back:
v.push_back(std::make_shared<EOne<K>>{});
To cast between the types you can use dynamic and static cast functions for smart pointers e.g. std::dynamic_pointer_cast<>().
Suppose I have two classes...
We can call the first FooReader and it looks something like this:
class FooReader {
public:
FooReader(const Foo* const foo)
: m_foo(foo) {
}
FooData readFooDataAndAdvance() {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
return m_foo[m_offset++];
}
private:
const Foo* const m_foo;
size_t m_offset = 0; // used in readFooDataAndAdvance
};
We can call the second FooWriter and it looks something like this:
class FooWriter {
public:
FooWriter(Foo* const foo)
: m_foo(foo) {
}
void writeFooDataAndAdvance(const FooData& foodata) {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
m_foo[m_offset++] = foodata;
}
private:
Foo* const m_foo;
size_t m_offset = 0;
};
These both work wonderfully and do their job as intended. Now suppose I want to create a FooReaderWriter class. Note that the
I naturally want to say that this new class "is a" FooReader and "is a" FooWriter; the interface is simply the amalgamation of the two classes and the semantics remain the same. I don't want to reimplement perfectly good member functions.
One could model this relationship using inheritance like so:
class FooReaderWriter : public FooReader, public FooWriter { };
This is nice because I get the shared interface, I get the implementation and I nicely model the relationship between the classes. However there are problems:
The Foo* member is duplicated in the base classes. This is a waste of memory.
The m_offset member is separate for each base type, but they need to share it (i.e. calling either readFooDataAndAdvance and writeFooDataAndAdvance should advance the same m_offset member).
I can't use the PIMPL pattern and store m_foo and m_offset in there, because I'd lose the const-ness of the m_foo pointer in the base FooReader class.
Is there anything else I can do to resolve these issues, without reimplementing the functionality contained within those classes?
This seems ready made for the mixin pattern. We have our most base class which just declares the members:
template <class T>
class members {
public:
members(T* f) : m_foo(f) { }
protected:
T* const m_foo;
size_t m_offset = 0;
};
and then we write some wrappers around it to add reading:
template <class T>
struct reader : T {
using T::T;
Foo readAndAdvance() {
return this->m_foo[this->m_offset++];
};
};
and writing:
template <class T>
struct writer : T {
using T::T;
void writeAndAdvance(Foo const& f) {
this->m_foo[this->m_offset++] = f;
}
};
and then you just use those as appropriate:
using FooReader = reader<members<Foo const>>;
using FooWriter = writer<members<Foo>>;
using FooReaderWriter = writer<reader<members<Foo>>>;
CRTP.
template<class Storage>
class FooReaderImpl {
public:
FooData readFooDataAndAdvance() {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
return get_storage()->m_foo[get_storage()->m_offset++];
}
private:
Storage const* get_storage() const { return static_cast<Storage const*>(this); }
Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class Storage>
class FooWriterImpl {
public:
void writeFooDataAndAdvance(const FooData& foodata) {
// the point here is that the algorithm is stateful
// and relies upon the m_offset member
get_storage()->m_foo[get_storage()->m_offset++] = foodata;
}
private:
Storage const* get_storage() const { return static_cast<Storage const*>(this); }
Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class T>
struct storage_with_offset {
T* m_foo = nullptr;
std::size_t m_offset = 0;
};
struct FooReader:
FooReaderImpl<FooReader>,
storage_with_offset<const Foo>
{
FooReader(Foo const* p):
storage_with_offset<const Foo>{p}
{}
};
struct FooWriter:
FooWriterImpl<FooWriter>,
storage_with_offset<Foo>
{
FooWriter(Foo* p):
storage_with_offset<Foo>{p}
{}
};
struct FooReaderWriter:
FooWriterImpl<FooReaderWriter>,
FooReaderImpl<FooReaderWriter>,
storage_with_offset<Foo>
{
FooReaderWriter(Foo const* p):
storage_with_offset<Foo>{p}
{}
};
If you need an abstract interface for runtime polymorphism, inherit FooReaderImpl and FooWriterImpl from them.
Now, FooReaderWriter obeys the ducktype contract of FooReader and FooWriter. So if you use type erasure instead of inheritance, it will qualify for either (at point of use).
I'd be tempted to change them to
using FooReader = std::function<FooData()>;
using FooWriter = std::function<void(FooData const&)>;
and then implement a multi-signature std::function for FooReaderWriter. But I'm strange and a bit unhinged that way.
Suppose I'm writing a container class template <typename T> class MyContainer and for some reason I would like to pass objects of type T with their type hidden from the user. In particular I would like to ensure that any input to MyContainer::Foo goes first through MyContainer::HideType.
template <typename T>
class MyContainer {
public:
... constructors and stuff...
HidesType& HideType(T&);
T& UnhideType(HidesType&);
void Foo(HideType&);
... some other stuff...
};
Now I am wondering what HidesType can/should be. Some options are:
struct HidesType {
HidesType(T& data) : data_(data) { }
T& data_;
};
HidesType HideType(T& data) { return HidesType(data); }
union HidesType { T data_; };
HidesType& HideType(T& data) { return reinterpret_cast<HidesType&>(data); }
class HidesType : public T { };
HidesType& HideType(T& data) { return static_cast<HidesType&>(data); }
The problem with the first is that user can't keep T objects as
MyContainer<T>::HidesType tmp(HideType(t_obj));
because tmp will be invalid as soon as t_obj goes out of scope. Also, I'm not sure if the compiler will optimize away the HidesType completely.
The problem with the second is that I don't know if the reinterpret_cast is safe (e.g. won't there be any alignment issues?).
The problem with the third is that T might be marked final and HidesType& converts to T& implicitly.
Any thoughts or suggestions will be greatly appreciated.
Edit:
The main purpose of hiding is to make HidesType abstract and distinguishable from T.
It would be troublesome to explain all the context which led to this question. For now, let's assume that the function MyContainer::Foo essentially takes as an input T, however, I don't want the user to know/use that, in particular the interface might change in the future.
Correct usecase could be:
MyContainer<T>::HidesType handle = MyContainer<T>::HideType(t_obj);
... do something ...
... perhaps t_obj.~T(); ...
... do something ...
my_container.Foo(handle);
Invalid usecase:
MyContainer<T>::HidesType handle = MyContainer<T>::HideType(t_obj);
... do something ...
my_container.Foo(some_other_t_obj);
It appears that you want a type H that can only be instantiated or modified by MyContainer<T>.
An easy way to achieve that is a class type with private constructors and assignment operator, except move constructor, and having MyContainer<T> as friend.
You can move the specified T object into it, both for efficiency and in order to not needlessly require it to be copyable. If so then H will not guarantee to be copyable. But it should, as I see it, be movable.
You may do something like:
template <typename T>
class MyContainer {
public:
class HandleType {
public:
friend class MyContainer;
HandleType(const HandleType& rhs) = default;
HandleType(HandleType&& rhs) = default;
HandleType& operator =(const HandleType& rhs) = default;
HandleType& operator =(HandleType&& rhs) = default;
private:
// only MyContainer can construct this class
explicit HandleType(const T& t) : t(t) {}
private:
T t;
};
HandleType HideType(const T& t) { return HandleType(t); }
T& UnhideType(HandleType& handle) { return handle.t; }
void Foo(HandleType&);
};
I am not sure what to call it, but is something like this possible as the commented out line reflects?
template <typename T>
class Test
{
public:
Test(T& t) : m_t(t) {}
T* operator->() { return &m_t; }
private:
T& m_t;
};
class A
{
public:
static const int integer = 0;
void function() {}
};
int main()
{
A a;
Test<A> test(a);
test->function();
// Something similar to doing Test<A>::integer?
return 0;
}
Well, why don't you do:
test->integer;
You can always access static members the same way as non-static ones (i.e. from an instance variable).
The other option would be to define in Test:
template <typename T>
class Test
{
public:
typedef T value_type;
// ...
};
In which case you will be able to do:
Test<A>::value_type::integer;
which will avoid the need of creating an instance of Test<A>.
At last, if you are using C++11 and Test follows the smart pointers conventions, then you will have:
std::pointer_traits<Test<A> >::element_type::integer;
which has the advantage to work even if you replace Test<A> with A*.
No. In C++, "overloading" only makes sense for functions. Instead of mixing static and non-static items in a class, you could try making two separate classes, both with all non-static members. Return your value from a function call, rather than using a public static member variable.