I'm using boost::variant to implement type erasure when sending data across network. When the data arrives to the client or server, I'm inspecting its structure to retrieve the underlying information and reporting errors when the structure does not match the one agreed upon in the API. For this purpose, I created a lightweight reference wrapper that holds a reference to the variant and provides a convenient interface for structure inspection and type conversions.
using value_t = boost::make_recursive_variant< /*types, some of them recursive*/ >::type;
class view_t {
public:
/*non-const methods to access value_*/
private:
value_t& value_;
};
class cview_t {
public:
/*const methods to access value_*/
private:
const value_t& value_;
};
view_t view(value_t& v){ return v; }
cview_t cview(const value_t& v){ return v; }
cview_t cview(value_t&& v) = delete;
cview_t cview(const value_t&& v) = delete;
Unfortunately, this interface proves to be cumbersome because every aggregate type that has a view member needs to follow the same const/non-const split. I was wondering whether it was legal to merge these two classes into a single view and use a const_cast in the methods that create views like this:
class view_t {
public:
/*const and non-const methods to access value_*/
private:
value_t& value_;
};
view_t view(value_t& v){ return v; }
const view_t cview(const value_t& v){ return const_cast<value_t&>(v); }
const view_t cview(value_t&& v) = delete;
const view_t cview(const value_t&& v) = delete;
Returning const object is mostly useless, as
auto /*view_t*/ my_view = cview(some_value); // legal
So you lose your constness.
Modifying const value is undefined behavior,
your view can be used in a way leading to UB.
But as long you don't modify const object, you are "fine".
Splitting into 2 classes is safer, as it cannot be misused.
Related
If I have the following:
class Animal {};
class Penguin : public Animal {};
class Snake : public Animal {};
class Zoo
{
std::vector<std::shared_ptr<Animal>> animals;
public:
const std::vector<std::shared_ptr<Animal>>& GetAnimals() { return animals; }
std::shared_ptr<Penguin> AddPenguin()
{
auto result = std::make_shared<Penguin>();
animals.push_back(result);
return result;
}
std::shared_ptr<Snake> AddSnake()
{
auto result = std::make_shared<Snake>();
animals.push_back(result);
return result;
}
};
I'd like to keep const correctness, and be able to add the following method:
const std::vector<std::shared_ptr<const Animal>>& GetAnimals() const
{
return animals;
}
However, that doesn't compile because the return type doesn't match animals. As the const is embedded deep in the type, const_cast isn't able to convert.
However, this compiles and appears to behave:
const std::vector<std::shared_ptr<const Animal>>& GetAnimals() const
{
return reinterpret_cast<const std::vector<std::shared_ptr<const Animal>>&>(animals);
}
I realize reinterpret_casts can be dangerous, but are there any dangers to use it in this scenario? Do the types being cast between have the same memory layout? Is the only effect of this to prevent the caller from then calling any non-const methods of Animal?
Update
I've realized this isn't entirely const correct. The caller could call .reset() on one of the elements of the vector, which would still modify the zoo. Even so, I'm still intrigued what the answer is.
Update on the update
I got that wrong, the code I was trying accidentally copied the shared_ptr and so the shared_ptr in the vector can't be reset when the vector is const.
std::shared_ptr<Animal> and std::shared_ptr<const Animal> are fundamentally different types. Messing with reinterpret_cast can lead to very strange bugs down the road (mostly due to optimizations, I would imagine). You have two options: create a new std::shared_ptr<const Animal> for each std::shared_ptr<Animal>, or return a complex proxy type (something like a view of the vector).
That said, I question the need for GetAnimals. If Zoo is meant to be a collection of pointers to animals, can't you provide access functions like size, operator[], and perhaps iterators? This does involve more effort, but if all you want is a function that returns the whole vector, why have a Zoo class in the first place? If Zoo contains other data and manages more than just a vector of animals, then I would make a separate class to take care of that part, AnimalList or something. That class can then provide appropriate access functions.
Something else you might try is to keep a std::shared_ptr<std::vector<Animal>> instead that you can easily convert into a std::shared_ptr<const std::vector<Animal>>. That may or may not be relevant depending on the reason you need shared pointers.
You can probably solve your problem with std::experimental::propagate_const. It is a wrapper for pointer-like types that properly propagates const-correctness.
A const std::shared_ptr<Animal> holds a mutable Animal. Retrieving a mutable reference to the mutable animal is legal, because the pointer itself is not changed. Vice versa, a std::shared_ptr<Animal const> will always hold a const Animal. You would have to explicitly cast away constness to mutate the held element which is ugly to say the least. Dereferencing a std::experimental::propagate_const<std::shared_ptr<Animal>> on the other hand returns a Animal const& if it is const, and a Animal& if it is not const.
If you wrap your shared pointers in std::experimental::propagate_const you can equip Zoo with a const and a non-const getter for your animals vector and have const-correctness (or you could make animals a public data member, since the getters don't do anything special. This would make your API more transparent):
#include <vector>
#include <memory>
#include <experimental/propagate_const>
#include <type_traits>
class Animal {};
class Penguin : public Animal {};
class Snake : public Animal {};
class Zoo
{
template <typename T>
using pointer_t = std::experimental::propagate_const<std::shared_ptr<T>>;
std::vector<pointer_t<Animal>> animals;
public:
// const-getter
auto const& GetAnimals() const
{
return animals;
}
// non-const getter
auto& GetAnimals()
{
return animals;
}
std::shared_ptr<Penguin> AddPenguin()
{
auto result = std::make_shared<Penguin>();
animals.push_back(result);
return result;
}
std::shared_ptr<Snake> AddSnake()
{
auto result = std::make_shared<Snake>();
animals.push_back(result);
return result;
}
};
int main() {
Zoo zoo;
zoo.AddSnake();
// non-const getter will propagate mutability through the pointer
{
auto& test = zoo.GetAnimals()[0];
static_assert(std::is_same<Animal&, decltype(*test)>::value);
}
// const-getter will propagate const through the pointer
{
Zoo const& zc = zoo;
auto& test = zc.GetAnimals()[0];
static_assert(std::is_same<Animal const&, decltype(*test)>::value);
}
return 0;
}
https://godbolt.org/z/1rd8YraMc
The only downsides I can think of are the discouraging "experimental" namespace and the fact that afaik MSVC hasn't implemented it yet, so it is not as portable as it could be....
If that bothers you, you can write your own propagate_const wrapper type, as #Useless suggested:
template <typename Ptr>
class propagate_const
{
public:
using value_type = typename std::remove_reference<decltype(*Ptr{})>::type;
template <
typename T,
typename = std::enable_if_t<std::is_convertible<T, Ptr>::value>
>
constexpr propagate_const(T&& p) : ptr{std::forward<T>(p)} {}
constexpr value_type& operator*() { return *ptr; }
constexpr value_type const& operator*() const { return *ptr; }
constexpr value_type& operator->() { return *ptr; }
constexpr value_type const& operator->() const { return *ptr; }
private:
Ptr ptr;
};
https://godbolt.org/z/eGPPPxef4
I am writing some C++ code which wraps the std::unordered_map type, where I want to hide the underlying type and present it as another type. More specifically, I want to wrap the std::pair from the std::unordered_map with another type. For the sake of argument, lets suppose the wrapper looks like this...
template <typename ActualT >
class wrapper final
{
private:
ActualT actual_;
public:
//Some constructors...
typename ActualT::first_type & get_first()
{
return actual_.first;
}
typename ActualT::second_type & get_second()
{
return actual_.second;
}
};
My reasoning is that since the wrapper class only has a member which is the exact type which it is wrapping, converting a reference from the original type to the wrapper type should be fine, but the type compatibility for structs states that the members should have the same type and name for the types to be compatible. Would using type-punning in this fashion potentially cause undefined behaviour or alignment issues?
using my_map = std::unordered_map < int, int >;
my_map m;
//Do some inserts...
reinterpret_cast<wrapper<typename my_map::value_type>&>(*m.find(10)).get_second() = 1.0;
I want client code to be allowed to access the entries of a map without knowing about the pair which is returned by the map. I also want to write a custom forward iterator, hence I need to return a reference to the entry. Would converting the reference to the pair to a reference to a class which act as a wrapper be considered dangerous?
Is there perhaps a better approach to accomplishing this?
This absolutely is undefined behaviour.
Seriously rethink your priorities.
Some free functions of the form
const my_map::key_type & MeaningfulNameHere(my_map::reference)
will go a long way to giving you meaningful names.
If you must wrap the standard library with different names, just use a non-explicit constructor, and store references.
template <typename Map>
class entry final
{
private:
typename Map::reference ref;
public:
entry(Map::reference ref) : ref(ref) {}
const typename Map::key_type & key()
{
return ref.first;
}
typename Map::mapped_type & value()
{
return ref.second;
}
};
If you really need the iterator to dereference to entry you can. But you can just implicitly instantiate entrys from the Map::references returned by Map::iterator::operator*, you don't need a custom iterator.
template <typename Map>
class entry_iterator
{
private:
typename Map::iterator it;
entry<Map> entry;
public:
entry<Map>& operator*() { return entry; }
entry_iterator operator++() { ++it; entry = *it; return *this; }
// etc
}
So you could clean this up, but I wouldn't suggest it:
#include <unordered_map>
#include <iostream>
using namespace std;
template <class Key, class Value>
class wrapper
{
public:
explicit wrapper(std::pair<const Key, Value>& kvp)
: _key{kvp.first}
, _value{kvp.second}
{}
const Key& key() const { return _key; }
Value& value() { return _value; }
private:
const Key& _key;
Value& _value;
};
int main()
{
unordered_map<int,int> m;
m[1] = 1;
m[3] = 3;
auto it = m.find(1);
wrapper w{*it};
w.value() = 30;
std::cout << w.key() << " -> " << w.value() << '\n';
}
The above effectively hides the pair from users of your class. It doesn't deal with exceptions (find() returning end() for example), and makes no guarantees about lifetimes. It's marginally better than what you have because it doesn't require a reinterpret_cast to an unrelated type.
However, map, unordered_map, set, etc. storing returning iterators as pairs is just part of library -- it's the canonical form and I don't see the benefit of shielding people from it.
I have two types of std::unique_ptr which are held inside a boost::variant. I'm trying to write a subclass of boost::static_visitor to extract a const reference to to the underlying object the two unique_ptr variants my boost::variant is templated on. The setup looks something like this:
using bitmap_flyweight_t = boost::flyweights::flyweight<allegro_bitmap_t>;
using image_bitmap_flyweight_t = boost::flyweights::flyweight<boost::flyweights::key_value<const char*, allegro_bitmap_t>>;
class bitmap_visitor : public boost::static_visitor<allegro_bitmap_t>
{
public:
const allegro_bitmap_t& operator()(const std::unique_ptr<bitmap_flyweight_t> bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
const allegro_bitmap_t& operator()(const std::unique_ptr<image_bitmap_flyweight_t> bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
};
I'm able to put the unique_ptrs into an object's boost::variant member variable on instantiation using move semantics, no compiler complaints there. However, when I try to access the variant type using the above visitor, the compiler complains that it can't do it, as unique_ptr is not copy constructible.
Accept a const reference to the unique pointer.
class bitmap_visitor : public boost::static_visitor<allegro_bitmap_t>
{
public:
const allegro_bitmap_t& operator()(const std::unique_ptr<bitmap_flyweight_t>& bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
const allegro_bitmap_t& operator()(const std::unique_ptr<image_bitmap_flyweight_t>& bitmap_ptr) const
{
return bitmap_ptr.get()->get();
}
};
Here's a live demo of a toy project.
I would like to refactor the accessors in following structure:
template<class T>
class ValueTime {
public:
// accessors for val:
const T& get_val() const { return val; }
template<class V> void set_val(const V& v) { val = v; }
// other accessors for tp
private:
T val;
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
};
I would like to make the accessors to the val data members more useful and intuitive, mostly from the point of view of the "standard/boost user expectations" of such structure representing a "value in time":
template<class V = T> V get_val() { return V(val); }
T& operator*() & { return val; }
const T& operator*() const & { return val; }
Now I can use the accessors this way (see the comments):
int main() {
ValueTime<double> vt;
// get_val() no longer returns const ref and also
// allows explicit cast to other types
std::chrono::minutes period{vt.get_val<int>()}; // I had to use the more pedantic static_cast<int> with the original version
// let's use operator*() for getting a ref.
// I think that for a structure like a ValueTime structure,
// it's clear that we get a ref to the stored "value"
// and not to the stored "time_point"
auto val = *vt; // reference now;
val = 42;
}
Is the getter more usueful now? Do you see anything strange or unsafe or counterintuitive in the new interface (apart from being non backward compatible, which I do not care)?
Furthermore, one doubt I still have is if it's better to implement get_val() by returning V(val) or V{val} or just val. As it is now, it works if V has an explicit constructor. What do you think about this issue?
I personally would advise you to make the interface as descriptive as possible and avoid any convenient conversions to reference of data or similar.
The reason is simply usability and maintenance. If you or somebody else are (re-)visiting code using ValueTime, when you cannot remember the precise interface, you still want to understand your code without re-visiting the definition of ValueTime.
There is a difference to members from std (such as std::vector) is that you know their definition by heart.
In various situations I have a collection (e.g. vector) of objects that needs to be processed by a number of functions. Some of the functions need to modify the objects while others don't. The objects' classes may inherit from an abstract base class. Hence, I have something like this:
class A
{
public:
virtual void foo() const = 0;
virtual void bar() = 0;
/* ... */
};
void process_1(std::vector<std::reference_wrapper<A>> const &vec);
void process_2(std::vector<std::reference_wrapper<A const>> const &vec);
Obviously (?) I can't pass the same vector of std::reference_wrapper<A>s to both process_1 and process_2. Solutions I've considered so far include:
Using a C-style cast or reinterpret_cast on a reference to vec
Writing my own reference wrapper that has T& get() and T const & get() const instead of T& get() const
Refactoring with e.g. methods that take a wrapper instead of the vector
Having copies of the vector with and without const
Not using const in reference_wrapper's argument
None of these seems very elegant. Is there something else I could do?
Range adapters.
A range adapter takes a range as input (a container is a range, as it has begin and end returning iterators), and returns a range with different properties.
You'd cast your reference wrappers to the const variant when you dereference the iterator.
boost has iterators that will do this for you (transform iterators), and tools to help write conforming iterators, but it can be done from scratch with some work.
A bit of extra work could even keep the typenames sane.
Even lacking elegance, I would make a reference wrapper:
#include <functional>
template <typename T>
class ReferenceWrapper
{
public:
ReferenceWrapper(T& ref)
: m_ref(ref)
{}
ReferenceWrapper(const std::reference_wrapper<T>& ref)
: m_ref(ref)
{}
const T& get() const noexcept { return m_ref.get(); }
T& get() noexcept { return m_ref.get(); }
operator const T& () const noexcept { return m_ref.get(); }
operator T& () noexcept { return m_ref.get(); }
private:
std::reference_wrapper<T> m_ref;
};
It is a tiny class modeling the original requirements.