I have been tidying up some old C++ code.
I've reduced pages of operator overload functions using a couple of macros:
.hpp
// for context, Long and Object wrap respective Python primitives
class Long: public Object
{...};
#define OPS( A, B ) \
bool operator == ( A, B ); \
bool operator != ( A, B ); \
bool operator > ( A, B ); \
bool operator < ( A, B ); \
bool operator >= ( A, B ); \
bool operator <= ( A, B );
#define UNI( A ) \
OPS( A, A )
#define BI_( A, B ) \
OPS( A, B ) \
OPS( B, A )
UNI( const Long& )
BI_( const Long& , int )
BI_( const Long& , long )
UNI( const Float& )
BI_( const Float& , double )
#undef BI_
#undef UNI
#undef OPS
#undef OP
.cpp
#define OP( op, l, r, cmpL, cmpR ) \
bool operator op( l, r ) { return cmpL op cmpR; }
#define OPS( ... ) \
OP( != , ##__VA_ARGS__ ) \
OP( == , ##__VA_ARGS__ ) \
OP( > , ##__VA_ARGS__ ) \
OP( >= , ##__VA_ARGS__ ) \
OP( < , ##__VA_ARGS__ ) \
OP( <= , ##__VA_ARGS__ )
#define BI_( a, b, convA, convB ) \
OPS( a, b, convA, convB ) \
OPS( b, a, convB, convA )
OPS( const Long& a, const Long& b , a.as_long() , b.as_long() )
BI_( const Long& a, int b , a.as_long() , b )
BI_( const Long& a, long b , a.as_long() , b )
OPS( const Float& a, const Float& b , a.as_double() , b.as_double() )
BI_( const Float& a, double b , a.as_double() , b )
#undef BI_
#undef OPS
#undef OP
It still doesn't feel quite right.
It spreads the class over three separate locations: the class declaration itself, declarations for the operators later in the same header, and then in a separate .cxx file the actual definitions.
Is there a cleaner way to implement these operators in C++11?
Why not use a CRTP base class to provide all the operators?
template<typename C, typename T>
struct ops_base
{
friend bool operator==(const ops_base& l, const ops_base& r)
{
const C& cl = static_cast<const C&>(l);
const C& cr = static_cast<const C&>(r);
return (caster<C, T>::cast(cl) == caster<C, T>::cast(cr));
}
friend bool operator==(const ops_base& l, T r)
{
const C& cl = static_cast<const C&>(l);
return (caster<C, T>::cast(cl) == r);
}
friend bool operator==(T l, const ops_base& r)
{
const C& cr = static_cast<const C&>(r);
return (l == caster<C, T>::cast(cr));
}
friend bool operator!=(const ops_base& l, const ops_base& r)
{ return !(l == r); }
friend bool operator!=(const ops_base& l, T r)
{ return !(l == r); }
friend bool operator!=(T l, const ops_base& r)
{ return !(l == r); }
friend bool operator<(const ops_base& l, const ops_base& r)
{
const C& cl = static_cast<const C&>(l);
const C& cr = static_cast<const C&>(r);
return (caster<C, T>::cast(cl) < caster<C, T>::cast(cr));
}
friend bool operator<(const ops_base& l, T r)
{
const C& cl = static_cast<const C&>(l);
return (caster<C, T>::cast(cl) < r);
}
friend bool operator<(T l, const ops_base& r)
{
const C& cr = static_cast<const C&>(r);
return (l < caster<C, T>::cast(cr));
}
friend bool operator>(const ops_base& l, const ops_base& r)
{
const C& cl = static_cast<const C&>(l);
const C& cr = static_cast<const C&>(r);
return (caster<C, T>::cast(cl) > caster<C, T>::cast(cr));
}
friend bool operator>(const ops_base& l, T r)
{
const C& cl = static_cast<const C&>(l);
return (caster<C, T>::cast(cl) > r);
}
friend bool operator>(T l, const ops_base& r)
{
const C& cr = static_cast<const C&>(r);
return (l > caster<C, T>::cast(cr));
}
friend bool operator<=(const ops_base& l, const ops_base& r)
{ return !(l > r); }
friend bool operator<=(const ops_base& l, T r)
{ return !(l > r); }
friend bool operator<=(T l, const ops_base& r)
{ return !(l > r); }
friend bool operator>=(const ops_base& l, const ops_base& r)
{ return !(l < r); }
friend bool operator>=(const ops_base& l, T r)
{ return !(l < r); }
friend bool operator>=(T l, const ops_base& r)
{ return !(l < r); }
};
And then use it like this:
struct Long : ops_base<Long, long>
{
Long(long val) : value_(val) { }
long as_long() const { return value_; }
private:
long value_;
};
struct Float : ops_base<Float, double>
{
Float(double val) : value_(val) { }
double as_double() const { return value_; }
private:
double value_;
};
Running example on ideone.
Related
I am working on a factory that can register and create classes with a different set a parameter types and numbers. I search over the internet and I managed to create this class:
template< typename Key, typename BaseClass >
class Factory {
static_assert( std::has_virtual_destructor< BaseClass >::value,
"BaseClass must have a virtual destructor" );
public:
template< typename DerivedClass, typename ... Args >
void register_creator( const Key& key )
{
static_assert( std::is_base_of< BaseClass, DerivedClass >::value,
"DerivedClass must be a subclass of BaseClass" );
static_assert( std::is_constructible< DerivedClass, Args... >::value,
"DerivedClass must be constructible with Args..." );
creators_.emplace(
CreatorKey { key, create_function_type_index< Args... >() },
reinterpret_cast< CreateFunc< > >( create_function_impl<
DerivedClass, Args... > ) );
}
template< typename ... Args >
std::unique_ptr< BaseClass > create(
const Key& key,
Args&&... args ) const
{
auto creator = creators_.find(
{ key, create_function_type_index< Args... >() } );
if( creator != creators_.end() ) {
return reinterpret_cast< CreateFunc< Args... > >( creator->second )(
std::forward< Args>( args )... );
} else {
return {};
}
}
private:
template< typename ... Args >
static std::type_index create_function_type_index()
{
return {typeid( CreateFunc<Args...> )};
}
template< typename DerivedClass, typename ... Args >
static std::unique_ptr< BaseClass > create_function_impl(
const Args&... args )
{
return std::unique_ptr< BaseClass > { new DerivedClass {
std::forward< const Args& >( args )... } };
}
template< typename ... Args >
using CreateFunc = typename std::add_pointer< std::unique_ptr< BaseClass >( const Args&... ) >::type;
using CreatorKey = std::pair< Key, std::type_index >;
std::map< CreatorKey, CreateFunc< > > creators_;
};
My goal is to be able to run this kind of code:
class ___A___ {};
class ___B___ {};
class ___Base___ {
public:
virtual ~___Base___() = default;
protected:
___Base___( ___A___ a, ___B___ b ) : a_( a ), b_( b ) {
}
protected:
___A___ a_;
___B___ b_;
};
class ___Derived___: public ___Base___ {
public:
___Derived___( ___A___& a, ___B___& b ) : ___Base___( a, b ) {
}
};
class ___Derived2___: public ___Base___ {
public:
___Derived2___( ___A___ a, ___B___ b ) : ___Base___( a, b ) {
}
};
class ___Derived3___: public ___Base___ {
public:
___Derived3___( ___A___& a, ___B___ b ) : ___Base___( a, b ) {
}
};
Factory< std::string, ___Base___ > factory;
factory.register_creator< ___Derived___, ___A___ &, ___B___& >( "Derived" );
factory.register_creator< ___Derived2___, ___A___, ___B___ >( "Derived2" );
factory.register_creator< ___Derived3___, ___A___ &, ___B___ >( "Derived3" );
___A___ a;
___B___ b;
auto D = factory.create( "Derived", a, b );
auto D2 = factory.create( "Derived2", a, b );
auto D3 = factory.create( "Derived3", a, b );
The registers works perfectly for both reference and value parameters but I cannot manage to instiate them using the creators. During my debugging, I saw that all the parameters where given by reference and never by value.
Using deduced types like this:
auto D = factory.create( "Derived", a, b );
in order to do a cast is something you should never do. The type deduces here will be basically &decltype(a) (not decltype(&a); a reference to the type of a, not the type of a pointer to a).
return reinterpret_cast< CreateFunc< Args... > >( creator->second )(
std::forward< Args>( args )... );
this requires exact type matching. When this matches the type, it happens accidentally more than anything.
To do what you want, you have a whole pile of work to do, even if you want to only support "exact matching" of types.
Given a type T, there are a number of ways it can be used as an argument to a function.
It can be moved into the argument: T&&. A "sink" argument.
It can be duplicated into the argument: T. A "value" argument.
It can be referred to by the argument but guaranteed not to be modified, T const&. An "in" argument.
It can be referred to by the argument, and possibly modified, T&. An "in/out" argument.
The other options are possible, but are less interesting.
At the point where you register your function, you have to work out both the type T and which of the 4 above cases you are dealing with.
Then, at the point where you want to call your function, you have to work out the type T and which of the above 4 you support.
Then you have to inject the code to glue the incoming argument to the function to call.
We can simplify this by having fewer kinds of argument than the above 4 relatively commonly used in C++ code. For example, "value" can usually replace "sink" at the cost of one extra std::move construction.
If you don't want the caller to have to know exactly what kind of protocol the called type needs, you have to type erase the details.
Imagine an adapter:
template<class T>
struct arg_adapter;
it can be constructed from a T&&, T const& or T&. When it is constructed, it records a void* pointer to the incoming object, and which of the 3 categores of type it was passed.
It has a .get<U> method, where U can be any of T&&, T const& or T&. It checks if the .get is compatible; if it is, it casts the void* stored within to remove_reference_t<U>*, dereferences it, and static_cast<U>'s it.
If it fails, it throws an exception or calls exit(-1) or somesuch.
Now you store std::function< Out(arg_adapter<std::decay_t<Args>>...) > instead of std::function< Out(Args...) >.
You know, something like this:
template
struct tag_t {};
template<class Dest>
using p_getter = Dest(*)(void*);
template<class T>
struct arg_vtable {
p_getter<T&> ref = 0;
p_getter<T const&> cref = 0;
p_getter<T> value = 0;
p_getter<T&&> move = 0;
};
template<class Dest, class Src>
p_getter<Dest> make_getter() {
return [](void* ptr)->Dest{
return (Src&&)(*static_cast<std::decay_t<Src>*>(ptr));
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T const&> ) {
return {
0,
make_getter<T const&, T const&>(),
make_getter<T, T const&>(),
0
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T&> ) {
return {
make_getter<T&, T&>(),
make_getter<T const&, T&>(),
make_getter<T, T&>(),
0
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T&&> ) {
return {
0,
make_getter<T const&, T&&>(),
make_getter<T, T&&>(),
make_getter<T&&, T&&>(),
};
}
template<class T>
arg_vtable<T> make_arg_vtable( tag_t<T const&&> ) {
return make_arg_vtable( tag_t<T const&>{} );
}
template<class T, class U>
arg_vtable<T> make_arg_vtable( tag_t<T volatile&&> ) {
return make_arg_vtable( tag_t<T&&>{} );
}
template<class T, class U>
arg_vtable<T> make_arg_vtable( tag_t<T volatile&> ) {
return make_arg_vtable( tag_t<T&>{} );
}
template<class T, class U>
arg_vtable<T> const* get_arg_vtable( tag_t<U> tag ) {
static const arg_vtable<T> retval = make_arg_vtable<T>(tag);
return &retval;
}
template<class T>
struct arg_adapter {
arg_vtable<T> const* vtable = 0;
void* pvoid = 0;
template<class U>
arg_adapter( U&& u ):
vtable( get_arg_vtable<T>( tag_t<U&&>{} ) ),
pvoid( (void*)std::addressof(u) )
{}
T get(tag_t<T>) {
if (!vtable->value) throw std::invalid_argument("value");
return vtable->value(pvoid);
}
T&& get(tag_t<T&&>) {
if (!vtable->move) throw std::invalid_argument("move");
return vtable->move(pvoid);
}
T& get(tag_t<T&>) {
if (!vtable->ref) throw std::invalid_argument("ref");
return vtable->ref(pvoid);
}
T const& get(tag_t<T const&>) {
if (!vtable->ref) throw std::invalid_argument("cref");
return vtable->cref(pvoid);
}
};
template<class R, class...Args>
using adapt_function = std::function< R(arg_adapter<std::decay_t<Args>>...) >;
template<class R, class...Args, class F>
adapt_function<R, Args...> adapt_args( F&& f ) {
return [f=std::forward<F>(f)](arg_adapter<std::decay_t<Args>>... args)->R{
return f( args.get( tag_t<Args>{} )... );
};
}
using func_token = std::shared_ptr<void>;
template<class R, class...Args>
R invoke_token( func_token f, Args&&... args ) {
auto const* pf = static_cast<adapt_function<R, Args...>*>(f.get());
return (*pf)( std::forward<Args>(args)... );
}
template<class R>
struct factories {
std::map< std::string, func_token > funcs;
template<class...Args, class F>
void add( std::string s, F&& f ) {
funcs[s] = std::make_shared<adapt_function<R,Args...>>(adapt_args<R,Args...>(std::forward<F>(f)));
}
template<class...Args>
R invoke( std::string s, Args&&... args ) {
auto it = funcs.find(s);
if (it==funcs.end()) throw std::invalid_argument("s");
return invoke_token<R>( it->second, std::forward<Args>(args)... );
}
};
Live example
It needs forwarding overloads to arg_adapter::get like the ones for make_arg_vtable I suspect.
Overall, I think this is a bad idea, because the invokers only handle cv conversion and nothing else. Anything else causes a crash or other undefined behavior.
How to implement a random access iterator for a sequence of elements which less than a single byte? For example, 6 bits.
Code example to illustrate what I want:
template<typename T>
class val_iterator : std::iterator<std::random_access_iterator_tag, T>
{
// ???
};
template<typename T>
class val_container
{
void *_data;
public:
val_container(void *data): _data(data) {}
// ???
};
int main()
{
std::vector<unsigned int> vec =
{
0xA, // '00 1010'
0xE, // '00 1110'
0x1F,// '01 1111'
0x3F // '11 1111'
};
// 4 elements of 6 bits
const size_t byte_count = 4 * 6 / 8;
std::array<unsigned char, byte_count> bytes;
val_container<unsigned char> values(bytes.data());
val_iterator<unsigned char> val_it = values.begin();
for(auto it = vec.begin(); it != vec.end(); ++it, ++val_it)
*val_it = (unsigned char) *it;
// elements:
// '00 1010'_'00 1110'_'01 1111'_'11 1111'
// bytes in memory:
// '0010 1000'_'1110 0111'_'1111 1111'
assert(bytes[0] == 0x28); // '0010 1000'
assert(bytes[1] == 0xE7); // '1110 0111'
assert(bytes[1] == 0xFF); // '1111 1111'
assert(values[0] == 0xA); // '00 1010'
assert(values[1] == 0xE); // '00 1110'
assert(values[2] == 0x1F); // '01 1111'
assert(values[3] == 0x3F); // '11 1111'
return 0;
}
Or it is not random access iterator, but another category of iterators?
I also think that this byte sequence consists of repeating blocks of three bytes each, which store 4 elements each. Maybe it knowledge can be used in an iterator, but I don't know how, yet.
EDIT:
I was not right about the location of bits in the byte array. In little-endian bits will looks like this: image
And, I try to implement this iterator/container. I used this code for start, and implementation of std::vector<bool> to write reference of value.
template<size_t TBitCount, typename TDataType, typename TValueType>
struct val_help
{
static size_t vals_count_in_block;
static size_t bytes_count_in_block;
static TValueType max_value;
static bool init()
{
static bool inited = false;
if(inited)
return true;
constexpr size_t value_size_in_bits = sizeof(TValueType) * 8;
static_assert(TBitCount >= 1, "TBitCount must be at least 1 bit");
static_assert(TBitCount <= value_size_in_bits, "TValueType doesn't have enough bit");
static_assert(sizeof(TDataType) == 1, "sizeof TDataType must be 1 byte");
size_t bits = 0;
size_t data_size_in_bits = sizeof(TDataType) * 8;
do {
vals_count_in_block++;
bits += TBitCount;
} while (bits % data_size_in_bits != 0);
bytes_count_in_block = bits / 8;
inited = true;
return true;
}
static size_t get_byte_idx(size_t val_idx)
{
return val_idx * bytes_count_in_block / vals_count_in_block;
}
static size_t get_lo_shift(size_t val_idx)
{
return (val_idx * TBitCount) % 8;
}
};
template<size_t TBitCount, typename TDataType, typename TValueType>
size_t val_help<TBitCount, TDataType, TValueType>::vals_count_in_block = 0;
template<size_t TBitCount, typename TDataType, typename TValueType>
size_t val_help<TBitCount, TDataType, TValueType>::bytes_count_in_block = 0;
template<size_t TBitCount, typename TDataType, typename TValueType>
TValueType val_help<TBitCount, TDataType, TValueType>::max_value = (1 << TBitCount) - 1;
template<size_t TBitCount, typename TDataType, typename TValueType>
class val_reference
{
using h = val_help<TBitCount, TDataType, TValueType>;
TDataType* _data;
size_t _val_idx;
public:
val_reference() : _data(nullptr), _val_idx(0) {}
val_reference(TDataType* data, size_t val_idx) : _data(data), _val_idx(val_idx) {}
operator TValueType() const
{ return get_value(); }
val_reference& operator=(TValueType val)
{
set_value(val);
return *this;
}
val_reference& operator=(const val_reference rhs) const
{ return *this = TValueType(rhs); }
bool operator==(const val_reference rhs) const
{ return TValueType(*this) == TValueType(rhs); }
bool operator<(const val_reference rhs) const
{ return TValueType(*this) < TValueType(rhs); }
// ToDo other operation
size_t idx() const
{ return _val_idx; }
private:
TValueType get_value() const
{
size_t byte_idx = h::get_byte_idx(_val_idx);
auto ptr_to_val = reinterpret_cast<TValueType*>(_data + byte_idx);
size_t lo_shift = h::get_lo_shift(_val_idx);
size_t hi_shift = sizeof(TValueType) * 8 - lo_shift;
bool is_hi_valid = byte_idx + sizeof(TValueType) < h::bytes_count_in_block;
auto lo = *ptr_to_val >> lo_shift;
auto hi = is_hi_valid ? *(ptr_to_val + 1) << hi_shift : 0;
return (hi | lo) & h::max_value;
}
void set_value(TValueType value)
{
auto val = value & h::max_value;
size_t byte_idx = h::get_byte_idx(_val_idx);
auto ptr_to_val = reinterpret_cast<TValueType*>(_data + byte_idx);
size_t lo_shift = h::get_lo_shift(_val_idx);
size_t hi_shift = sizeof(TValueType) * 8 - lo_shift;
TValueType &lo = *ptr_to_val;
lo = (lo & ~(h::max_value << lo_shift)) | (val << lo_shift);
bool is_hi_valid = byte_idx + sizeof(TValueType) < h::bytes_count_in_block;
if(is_hi_valid)
{
TValueType &hi = *(ptr_to_val + 1);
hi = (hi & ~(h::max_value >> hi_shift)) | (val >> hi_shift);
}
}
};
template<size_t TBitCount, typename TDataType, typename TValueType>
class val_iterator : public std::iterator<std::random_access_iterator_tag, TValueType>
{
using h = val_help<TBitCount, TDataType, TValueType>;
TDataType *_data;
size_t _val_idx;
public:
using reference = val_reference<TBitCount, TDataType, TValueType>;
using pointer = val_reference<TBitCount, TDataType, TValueType>*;
using iterator = val_iterator<TBitCount, TDataType, TValueType>;
using difference_type = int;
val_iterator(TDataType* data) : _data(data), _val_idx(0){}
val_iterator(TDataType* data, unsigned int val_idx) : _data(data), _val_idx(val_idx){}
val_iterator(const iterator& rhs) : _data(rhs._data), _val_idx(rhs._val_idx) {}
iterator& operator=(const iterator& rhs)
{
_data = rhs._data;
_val_idx = rhs._val_idx;
return *this;
}
reference operator*() const
{ return reference(_data, _val_idx); }
reference operator[](const difference_type& n) const
{ return *(*this + n); }
iterator& operator++()
{
if(_val_idx == h::vals_count_in_block - 1)
{
_data += h::bytes_count_in_block;
_val_idx = 0;
}
else
{
++_val_idx;
}
return *this;
}
iterator& operator--()
{
if(_val_idx == 0)
{
_data -= h::bytes_count_in_block;
_val_idx = h::vals_count_in_block - 1;
}
else
{
--_val_idx;
}
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
++(*this);
return tmp;
}
iterator operator--(int)
{
iterator tmp(*this);
--(*this);
return tmp;
}
iterator& operator+=(const difference_type& n)
{
auto idx = _val_idx + n;
_data += (idx / h::vals_count_in_block) * h::bytes_count_in_block;
_val_idx = idx % h::vals_count_in_block;
return *this;
}
iterator operator+(const difference_type& n) const
{
iterator tmp(*this);
tmp += n;
return tmp;
}
iterator& operator-=(const difference_type& n)
{
if(n <= _val_idx)
{
_val_idx -= n;
return *this;
}
auto diff_idx = (n % h::vals_count_in_block) - _val_idx;
auto idx = n - diff_idx;
_data -= (idx / h::vals_count_in_block + 1) * h::bytes_count_in_block;
_val_idx = h::vals_count_in_block - diff_idx;
return *this;
}
iterator operator-(const difference_type& n) const
{
iterator tmp(*this);
tmp -= n;
return tmp;
}
bool operator==(const iterator& rhs) const
{
return _data == rhs._data && _val_idx == rhs._val_idx;
}
bool operator!=(const iterator& rhs) const
{
return !(*this == rhs);
}
bool operator<(const iterator& rhs) const
{
return _data == rhs._data
? _val_idx < rhs._val_idx
: _data < rhs._data;
}
bool operator<=(const iterator& rhs) const
{
return *this < rhs || *this == rhs;
}
bool operator>(const iterator& rhs) const
{
return !(*this <= rhs);
}
bool operator>=(const iterator& rhs) const
{
return !(*this < rhs);
}
};
template<size_t TBitCount, typename TDataType, typename TValueType,
typename std::enable_if<std::is_integral<TValueType>::value, int>::type = 0>
class val_container
{
using h = val_help<TBitCount, TDataType, TValueType>;
TDataType* _data;
size_t _size;
public:
using value_type = TValueType;
using size_type = size_t;
using difference_type = int;
using reference = val_reference<TBitCount, TDataType, TValueType>;
using const_reference = TValueType;
using pointer = val_reference<TBitCount, TDataType, TValueType>*;
using const_pointer = const TValueType*;
using iterator = val_iterator<TBitCount, TDataType, TValueType>;
using const_iterator = val_iterator<TBitCount, TDataType, const TValueType>;
val_container(TDataType* data = nullptr, size_t size = 0) : _data(data), _size(size)
{
static_assert(sizeof(TDataType) == 1, "sizeof TDataType must be 1 byte");
static_assert(TBitCount >= 1, "TBitCount must be at least 1 bit");
static volatile bool s = h::init();
if(size % h::bytes_count_in_block != 0)
throw std::invalid_argument(std::string("size: ") + std::to_string(size)+ " % " + std::to_string(h::bytes_count_in_block) + " != 0");
}
val_container(TDataType& data, size_t size) : val_container(&data, size) {}
iterator begin()
{ return iterator(_data); }
const_iterator begin() const
{ return const_iterator(_data); }
const_iterator cbegin() const
{ return const_iterator(_data); }
iterator end()
{ return iterator(_data + _size); }
const_iterator end() const
{ return const_iterator(_data + _size); }
const_iterator cend() const
{ return const_iterator(_data + _size); }
size_type size() const
{ return _size; }
bool empty() const
{ return begin() == end(); }
reference operator[](size_type n)
{ return *(iterator(_data) + n); }
const_reference operator[](size_type n) const
{ return *(const_iterator(_data) + n); }
};
It work for any TBitCount (I think).
What can be improved in this code?
Firstly, to implement a random access iterator in general, you need to implement the operations that are required to fulfill the RandomAccessIterator concept.
You will need to implement addition and subtraction with integers and compound assignment for each, subtraction of two iterators, subscript operator, less/greather than operator, both strict and non-strict, (in-)equality operator, pre- and post-increment and -decrement, dereference and the arrow operator.
See the documentation of iterator concepts for exact required behaviour for each of those operations.
Secondly, you must consider the fact that the elements returned by the iterator are less than byte sized. Clearly, since a byte is the smallest addressable object, you cannot have a reference to such smaller object (or rather, such fragments of objects).
What you can do is use a custom proxy class as your val_iterator::reference. The behaviour of the proxy should be to modify the referenced bits when assigned to, and be implicitly convertible to the value of the referenced bit range. This is the same way the std::vector<bool>::reference is implemented.
I am currently working on a piece of code in which lots of pointers are used without proper ownership control. In some cases, it becomes a huge contraint because everything has to be initialized properly and can't be changed afterwards. So far I have been using dummy wrappers
// MyObject.h
struct MyObject
{
virtual int myMethod(int i){ return i; }
};
struct MyObjectWrapper : MyObject
{
MyObject *obj = nullptr;
int myMethod(int i){ if(obj) return obj->myMethod(i); }
void setObject(MyObject *obj){ this->obj = obj; }
}
// MyObjectFactory.h
MyObject *createMyObject(){
MyObject *object = new MyObject();
MyObjectWrapper *wrapper = new MyObjectWrapper();
wrapper->setObject(object);
return wrapper;
}
I am wondering if there is a way to do pointer "spoofing" by returning an object that is not actually a pointer but has the type of one, so that I can return a smart pointer instead of the regular pointer without changing the rest of the code. Is there a way of achieving that?
Additionnally, this led me to think about boost::optional. How is it implemented? Is boost using a lot of preprocessing for reflection? I cannot understand how it is possible to "intercept" a method call on a variable without neither knowing the name of the method nor using heavy preprocessing.
In case the answers are simple NOs, are you aware of any design pattern that can be used to work around my issues?
Ya know. It's time for the "Don't Defend Against Macchiavelli¹" talk. You'll always be able to know the address of an object if you have access to the object.
If you don't trust your users for that, don't give them access.
This is why the e.g. the OS kernel gives out opaque handles (aside from technical reasons).
In case your question was "Can I automatically create a proxy object" the answer is NO:
not without code generation tools
not without overloadable operator. (which doesn't currently exist outside of language proposals)
¹ e.g. http://www.gotw.ca/gotw/076.htm
You want a type that can store a dumb pointer or a smart pointer relatively transparently? Well, this isn't a good plan, but it can be done:
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
template<class T>
using dot_get_r = decltype( std::declval<T>().get() );
template<class T>
using can_get = can_apply< dot_get_r, T >;
template<class T>
struct ptr_like {
T* get()const{
if (!pvtable && !pimpl) return nullptr;
return pvtable->get(pimpl.get());
}
explicit operator bool() const{ return !!get(); }
// wall of == <= etc:
friend bool operator==( ptr_like const& ptr, std::nullptr_t ) { return !(bool)ptr; }
friend bool operator==( std::nullptr_t, ptr_like const& ptr ) { return !(bool)ptr; }
friend bool operator!=( ptr_like const& ptr, std::nullptr_t ) { return (bool)ptr; }
friend bool operator!=( std::nullptr_t, ptr_like const& ptr ) { return (bool)ptr; }
friend bool operator==( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()==rhs.get();}
friend bool operator!=( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()!=rhs.get();}
friend bool operator<=( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()<=rhs.get();}
friend bool operator>=( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()>=rhs.get();}
friend bool operator<( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()<rhs.get();}
friend bool operator>( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()>rhs.get();}
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator==( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()==rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator==( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()==rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator!=( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()!=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator!=( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()!=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<=( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()<=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<=( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()<=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>=( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()>=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>=( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()>=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()<rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()<rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()>rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()>rhs.get(); }
// -> and unary * support:
T* operator->()const{ return get(); }
T& operator*()const{ return *get(); }
// move:
ptr_like(ptr_like&&)=default;
ptr_like& operator=(ptr_like&&)=default;
// copy:
ptr_like(ptr_like const& o):
pvtable(o.pvtable),
pimpl(o.clone())
{}
ptr_like& operator=(ptr_like const& o) {
auto tmp = o;
swap(*this, tmp);
return *this;
}
// swap:
friend void swap( ptr_like& lhs, ptr_like& rhs ) {
using std::swap;
swap(lhs.vtable, rhs.vtable);
swap(lhs.pimpl, rhs.pimpl);
}
// Construct from smart pointer:
template<class Smart,
class dSmart = std::decay_t<Smart>,
std::enable_if_t< can_get<dSmart const&>{} && !std::is_same<dSmart, ptr_like>{}, int > =0
>
ptr_like( Smart&& smart ):
pvtable( smart_vtable<dSmart>() ),
pimpl( unique_ptr_with_deleter(std::forward<Smart>(smart)) )
{}
// construct from dumb pointer:
template<class Dumb>
ptr_like( Dumb* dumb ):
pvtable( pointer_vtable<Dumb>() ),
pimpl( unique_ptr_with_deleter(dumb) )
{}
private:
using state = std::unique_ptr<void, void(*)(void*)>;
struct vtable {
T* (*get)(void*) = 0;
state (*clone)(state const&) = 0;
};
vtable const* pvtable = nullptr;
state pimpl;
state clone() const {
if (!pimpl||!pvtable) return {nullptr, [](void*){}};
return pvtable->clone( pimpl );
}
template<class U,
class dU = std::decay_t<U>
>
static state unique_ptr_with_deleter(U&& u) {
std::unique_ptr<void, void(*)(void*)> r
= {
new dU(std::forward<U>(u)),
[](void* ptr){
delete static_cast<dU*>(ptr);
}
};
return r;
}
template<class U>
static vtable const* pointer_vtable() {
static vtable const table = make_ptr_vtable<U>();
return &table;
}
template<class O>
static vtable const* smart_vtable() {
static vtable const table = make_smart_vtable<O>();
return &table;
}
template<class U>
static vtable make_ptr_vtable() {
return {
[](void* ptr)->T*{
return *static_cast<U**>(ptr);
},
[](state const& ptr)->state{
return {ptr.get(), ptr.get_deleter()};
}
};
}
template<class O>
static vtable make_smart_vtable() {
return {
[](void* ptr)->T*{
O* o = static_cast<O*>(ptr);
return o?o->get():nullptr;
},
[](state const& ptr)->state{
O* o = static_cast<O*>(ptr.get());
if (!o) return {nullptr, ptr.get_deleter()};
return {new O(*o), ptr.get_deleter()};
}
};
}
};
note that unique_ptr does not qualify, as it cannot be copied while dumb pointers can. Custom smart pointers (that can be copied) can be used. We could extend this to having it throw/terminate if you pass it a non-copyable smart pointer and that value is copied instead of moved, if you where even more insane than this answer.
Live example. Code is C++1z, and no I'm not making it C++11 or 14, it is a dumb idea to start with.
Code is incomplete -- nullptr < p doesn't work, for example. Code is less than perfectly efficient (I could store dumb pointers more efficiently, and use SBO even for smart pointers). Needs std::less specialization or have < use it.
But it is a proof of concept.
int foo( ptr_like<int> ptr ) {
if (!ptr) return -1;
return *ptr+1;
}
will compile. The ptr_like<int> could be an int* or a std::shared_ptr<int> it won't care. You can persist the ptr_like<int> and copy it around. Copies of shared_ptr<int>s will maintain reference counts, copies of int* won't.
If you use this, you'll end up with a code base that is harder to maintain, not easier, as object lifetime will get more confusing, not less.
I would like to have compile-time selection of function pointers.
Something like functionListAutomatic in the following
int funk( int a, int b ) { return a * b / 2; }
template< typename T0, typename T1 >
int null_func( T0 a, T1 b ) { return 0; }
tuple< int( *)(int, int), int( *)(int, float) > functionList {
funk,
null_func<int, float>
};
// Pseudo code.
tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic {
condition( funk_exist( funk( int, int ) ) , funk, null_func<int, int> ),
condition( funk_exist( funk( int, string ) ), funk, null_func<int, string> ),
};
void main() {
int res0 = get<0>( functionList )(1, 2);
int res1 = get<1>( functionList )(1, 2);
}
I cannot figure out how to do this.
I do know how to make funk_exist so that it evaluates at compile time (I use a variant of this: https://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector). But the two parameters funk and null_func causes problem. The compiler tries to find a funk( int, string ) function and fails before it evaluates funk_exist(). I need an expression that evaluates funk_exist() and then does not evaluate funk( int, string ) if funk_exist() evaluates to false.
Appreciate your help.
namespace details {
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,void_t<Z<Ts...>>,Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
boilerplate injected!1
Adding funk:
template<class...Ts>
funk_r = decltype(funk(std::declval<Ts>()...));
template<class...Ts>
can_funk = can_apply<funk_r, Ts...>;
now we know if we can funk.
but who will funk? The funk_miester:
template<class lhs, class rhs, class=void>
struct funk_miester {
decltype(null_func<lhs,rhs>) operator()()const{
return null_func<lhs, rhs>;
}
};
template<class lhs, class rhs>
struct funk_miester<lhs, rhs, std::enable_if_t< can_funk<lhs,rhs>{} >> {
funk_r<lhs,rhs>(*)(lhs, rhs) operator()()const{
return [](lhs l, rhs r)->funk_r<lhs,rhs> {
return funk(l,r);
};
}
};
and get down below this line to see the result:
tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic (
funk_miester<int,int>{}(),
funk_miester<int,string>{}()
);
and you are funkey.
Note that I check if funk can be called in can_funk, but you can replace that with whatever trait you want, so long as it generates a compile-time bool.
In my case, the lambda acts as an adapter, so if the signatures don't match it will still generate a function pointer.
1 This just gives me a trait to detect if I can call funk on some arguments. You have your own, so you don't have to use it.
I would like to thank Yakk for his answer. With a few modifications I got that to work, and I have posted the complete program below.
However there were a few snafus.
The use of class template for Z in details pushes the evaluation out of details and out to funk_r. This means that the compiler tries to evaluate a funk( int, string ) which then gives a compile error. Apparently it is no possible to use SFINAE when making aliases with using so I did not find a solution for that.
#pragma once
#include <string>
#include <tuple>
using namespace std;
int funk( int a, int b ) { return a * b; }
template< typename T0, typename T1 >
int null_funk( T0 a, T1 b ) { return 0; }
template< typename... Ts >
struct can_apply {
using Yes = char[2];
using No = char[1];
// SFINAE will take 'Yes' if funk( Us... ) matches. Otherwise it will take 'No'
template< typename... Us >
static Yes & test( decltype(funk( Us()... ))* ); // *1
template< typename U0, typename... Us >
static No & test( U0* );
static constexpr bool value = sizeof( test< Ts... >( nullptr ) ) == sizeof( Yes );
};
template< typename... Ts >
using funk_r = decltype(funk( declval<Ts>()... ));
template< typename... Ts >
using can_funk = can_apply< Ts... >;
template< typename lhs, typename rhs, typename = void >
struct funk_meister {
typedef typename decltype(null_funk<lhs, rhs>( lhs(), rhs() ))(*TFunk)(lhs, rhs);
TFunk operator()() const {
return null_funk<lhs, rhs>;
}
};
template<typename lhs, typename rhs>
struct funk_meister<lhs, rhs, enable_if_t< can_funk<lhs, rhs>::value > > {
typedef typename funk_r<lhs, rhs>( *TFunk )(lhs, rhs);
TFunk operator()() const {
return []( lhs l, rhs r ) -> funk_r<lhs, rhs> {
return funk( l, r );
};
}
};
tuple< int( *)(int, int), int( *)(int, string), int( *)(int, float)> functionList {
funk_meister<int,int>{}(), funk_meister<int,string>{}(), funk_meister<int,float>{}()
};
void test() {
int res0 = get<0>( functionList )(1, 2);
int res1 = get<1>( functionList )(1, "2");
int res2 = get<2>( functionList )(1, 2.5f);
}
*1. It is not possible to use class template here because then the SFINAE evaluation gets pushed out to funk_r<...>.
The find method of boost::splay_set that require only the key accepts an argument of type KeyValueCompare to compare objects with the key. To be able to use this, we need to supply two methods of the form:
struct KeyValCompare {
inline bool operator() (const std::int64_t key, const MyType& val) const {
//TODO:
}
inline bool operator() (const MyType& val, const std::int64_t key) const {
//TODO:
}
};
However there is no mention in the documentation about how to implement these. Any pointers?
Found a solution here:
http://boost.cowic.de/rc/pdf/intrusive.pdf
they should return true if key (or key from the value) of lhs is less than the key (or key from the value) of rhs.
I don't see why the comparator would be so complicated. The set just stores elements of MyType, so you need to define a strict weak total ordering on them:
struct Comparator {
bool operator()(MyType const& a, MyType const& b) const;
};
Indeed, the default comparer is std::less<MyType>
E.g. to sort
class MyType : public splay_set_base_hook<>
{
int int_;
public:
MyType(int i) : int_(i) {}
int getValue() const { return int_; }
};
By the value, after reversing the digits (e.g. "431" before "322" because 134<223):
struct CompareReversed {
bool operator()(MyType const& a, MyType const& b) const {
return reversed(a.getValue()) < reversed(b.getValue());
}
private:
static int reversed(int i)
{
auto s = std::to_string(i);
std::reverse(s.begin(), s.end());
return boost::lexical_cast<int>(s);
}
};
See it Live On Coliru:
#include <boost/intrusive/splay_set.hpp>
#include <boost/lexical_cast.hpp>
#include <vector>
#include <algorithm>
using namespace boost::intrusive;
class MyType : public splay_set_base_hook<>
{
int int_;
public:
MyType(int i) : int_(i)
{}
// default ordering
friend bool operator< (const MyType &a, const MyType &b) { return a.int_ < b.int_; }
friend bool operator> (const MyType &a, const MyType &b) { return a.int_ > b.int_; }
friend bool operator== (const MyType &a, const MyType &b) { return a.int_ == b.int_; }
int getValue() const { return int_; }
};
struct CompareReversed {
bool operator()(MyType const& a, MyType const& b) const {
return reversed(a.getValue()) < reversed(b.getValue());
}
private:
static int reversed(int i)
{
auto s = std::to_string(i);
std::reverse(s.begin(), s.end());
return boost::lexical_cast<int>(s);
}
};
#include <iostream>
int main()
{
//typedef splay_set<MyType, compare<std::less<MyType> > > Set;
typedef splay_set<MyType, compare<CompareReversed> > Set;
std::vector<MyType> v { 24, 42, 123, 321 };
Set set;
set.insert(v[0]);
set.insert(v[1]);
set.insert(v[2]);
set.insert(v[3]);
for (auto& el : set)
{
std::cout << el.getValue() << "\n";
}
std::cout << set.count(24) << "\n"; // 1
std::cout << set.count(25) << "\n"; // 0
std::cout << set.count(42) << "\n"; // 1
}
If you want to suppor mixed type comparisons, just supply the overloads, obviously:
struct CompareReversed {
bool operator()(MyType const& a, MyType const& b) const {
return reversed(a.getValue()) < reversed(b.getValue());
}
bool operator()(MyType const& a, int b) const {
return reversed(a.getValue()) < reversed(b);
}
bool operator()(int a, MyType const& b) const {
return reversed(a) < reversed(b.getValue());
}
// ...
};
Thanks sehe for the support.
That is exactly what I am doing there. But please have a look at following sample code which fails.
#include <boost/intrusive/splay_set.hpp>
#include <algorithm>
using namespace boost::intrusive;
class MyClass {
public:
MyClass(const std::int64_t& k)
: key(k) {
}
std::int64_t key;
splay_set_member_hook<> member_hook_;
friend bool operator <(const MyClass& lhs, const MyClass& rhs) {
return lhs.key < rhs.key;
}
friend bool operator >(const MyClass& lhs, const MyClass& rhs) {
return lhs.key > rhs.key;
}
friend bool operator ==(const MyClass& lhs, const MyClass& rhs) {
return lhs.key == rhs.key;
}
};
struct KeyValCompare {
inline bool operator()(const std::int64_t key, const MyClass& val) const {
return key < val.key;
}
inline bool operator()(const MyClass& val, const std::int64_t key) const {
return val.key < key;
}
};
typedef member_hook<MyClass, splay_set_member_hook<>, &MyClass::member_hook_> MemberOption;
typedef splay_set<MyClass, MemberOption, compare<std::greater<MyClass> > > MyClassObjectsType;
TEST(MyClass, test) {
MyClassObjectsType set;
set.insert(*new MyClass(10));
set.insert(*new MyClass(20));
set.insert(*new MyClass(100));
auto ite = set.find(100, KeyValCompare());
ASSERT_TRUE(ite != set.end()); // Fails here
}
If I use std::less instead of std::greater, it passes.
Figured it out:
The greater than operator must be change from:
friend bool operator >(const MyClass& lhs, const MyClass& rhs) {
return lhs.key > rhs.key;
}
to this:
friend bool operator >(const MyClass& lhs, const MyClass& rhs) {
return lhs.key < rhs.key;
}