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<...>.
Related
I have an application with several boost::variants which share many of the fields. I would like to be able to compose these visitors into visitors for "larger" variants without copying and pasting a bunch of code. It seems straightforward to do this for non-recursive variants, but once you have a recursive one, the self-references within the visitor (of course) point to the wrong class. To make this concrete (and cribbing from the boost::variant docs):
#include "boost/variant.hpp"
#include <iostream>
struct add;
struct sub;
template <typename OpTag> struct binop;
typedef boost::variant<
int
, boost::recursive_wrapper< binop<add> >
, boost::recursive_wrapper< binop<sub> >
> expression;
template <typename OpTag>
struct binop
{
expression left;
expression right;
binop( const expression & lhs, const expression & rhs )
: left(lhs), right(rhs)
{
}
};
// Add multiplication
struct mult;
typedef boost::variant<
int
, boost::recursive_wrapper< binop<add> >
, boost::recursive_wrapper< binop<sub> >
, boost::recursive_wrapper< binop<mult> >
> mult_expression;
class calculator : public boost::static_visitor<int>
{
public:
int operator()(int value) const
{
return value;
}
int operator()(const binop<add> & binary) const
{
return boost::apply_visitor( *this, binary.left )
+ boost::apply_visitor( *this, binary.right );
}
int operator()(const binop<sub> & binary) const
{
return boost::apply_visitor( *this, binary.left )
- boost::apply_visitor( *this, binary.right );
}
};
class mult_calculator : public boost::static_visitor<int>
{
public:
int operator()(int value) const
{
return value;
}
int operator()(const binop<add> & binary) const
{
return boost::apply_visitor( *this, binary.left )
+ boost::apply_visitor( *this, binary.right );
}
int operator()(const binop<sub> & binary) const
{
return boost::apply_visitor( *this, binary.left )
- boost::apply_visitor( *this, binary.right );
}
int operator()(const binop<mult> & binary) const
{
return boost::apply_visitor( *this, binary.left )
* boost::apply_visitor( *this, binary.right );
}
};
// I'd like something like this to compile
// class better_mult_calculator : public calculator
// {
// public:
// int operator()(const binop<mult> & binary) const
// {
// return boost::apply_visitor( *this, binary.left )
// * boost::apply_visitor( *this, binary.right );
// }
// };
int main(int argc, char **argv)
{
// result = ((7-3)+8) = 12
expression result(binop<add>(binop<sub>(7,3), 8));
assert( boost::apply_visitor(calculator(),result) == 12 );
std::cout << "Success add" << std::endl;
// result2 = ((7-3)+8)*2 = 12
mult_expression result2(binop<mult>(binop<add>(binop<sub>(7,3), 8),2));
assert( boost::apply_visitor(mult_calculator(),result2) == 24 );
std::cout << "Success mult" << std::endl;
}
I would really like something like that commented out better_mult_expression to compile (and work) but it doesn't -- because the this pointers within the base calculator visitor don't reference mult_expression, but expression.
Does anyone have suggestions for overcoming this or am I just barking down the wrong tree?
Firstly, I'd suggest the variant to include all possible node types, not distinguishing between mult and expression. This distinction makes no sense at the AST level, only at a parser stage (if you implement operator precedence in recursive/PEG fashion).
Other than that, here's a few observations:
if you encapsulate the apply_visitor dispatch into your evaluation functor you can reduce the code duplication by a big factor
your real question seems not to be about composing variants, but composing visitors, more specifically, by inheritance.
You can use using to pull inherited overloads into scope for overload resolution, so this might be the most direct answer:
Live On Coliru
struct better_mult_calculator : calculator {
using calculator::operator();
auto operator()(const binop<mult>& binary) const
{
return boost::apply_visitor(*this, binary.left) *
boost::apply_visitor(*this, binary.right);
}
};
IMPROVING!
Starting from that listing let's shave off some noise!
remove unncessary AST distinction (-40 lines, down to 55 lines of code)
generalize the operations; the <functional> header comes standard with these:
namespace AST {
template <typename> struct binop;
using add = binop<std::plus<>>;
using sub = binop<std::minus<>>;
using mult = binop<std::multiplies<>>;
using expr = boost::variant<int,
recursive_wrapper<add>,
recursive_wrapper<sub>,
recursive_wrapper<mult>>;
template <typename> struct binop { expr left, right; };
} // namespace AST
Now the entire calculator can be:
struct calculator : boost::static_visitor<int> {
int operator()(int value) const { return value; }
template <typename Op>
int operator()(AST::binop<Op> const& binary) const {
return Op{}(boost::apply_visitor(*this, binary.left),
boost::apply_visitor(*this, binary.right));
}
};
Here your variant can add arbitrary operations without even needing to touch the calculator.
Live Demo, 43 Lines Of Code
Like I mentioned starting off, encapsulate visitation!
struct Calculator {
template <typename... T> int operator()(boost::variant<T...> const& v) const {
return boost::apply_visitor(*this, v);
}
template <typename T>
int operator()(T const& lit) const { return lit; }
template <typename Op>
int operator()(AST::binop<Op> const& bin) const {
return Op{}(operator()(bin.left), operator()(bin.right));
}
};
Now you can just call your calculator, like intended:
Calculator calc;
auto result1 = calc(e1);
It will work when you extend the variant with operatios or even other literal types (like e.g. double). It will even work, regardless of whether you pass it an incompatible variant type that holds a subset of the node types.
To finish that off for maintainability/readability, I'd suggest making operator() only a dispatch function:
Full Demo
Live On Coliru
#include <boost/variant.hpp>
#include <iostream>
namespace AST {
using boost::recursive_wrapper;
template <typename> struct binop;
using add = binop<std::plus<>>;
using sub = binop<std::minus<>>;
using mult = binop<std::multiplies<>>;
using expr = boost::variant<int,
recursive_wrapper<add>,
recursive_wrapper<sub>,
recursive_wrapper<mult>>;
template <typename> struct binop { expr left, right; };
} // namespace AST
struct Calculator {
auto operator()(auto const& v) const { return call(v); }
private:
template <typename... T> int call(boost::variant<T...> const& v) const {
return boost::apply_visitor(*this, v);
}
template <typename T>
int call(T const& lit) const { return lit; }
template <typename Op>
int call(AST::binop<Op> const& bin) const {
return Op{}(call(bin.left), call(bin.right));
}
};
int main()
{
using namespace AST;
std::cout << std::boolalpha;
auto sub_expr = add{sub{7, 3}, 8};
expr e1 = sub_expr;
expr e2 = mult{sub_expr, 2};
Calculator calc;
auto result1 = calc(e1);
std::cout << "result1: " << result1 << " Success? " << (12 == result1) << "\n";
// result2 = ((7-3)+8)*2 = 12
auto result2 = calc(e2);
std::cout << "result2: " << result2 << " Success? " << (24 == result2) << "\n";
}
Still prints
result1: 12 Success? true
result2: 24 Success? true
I am following this code snippet which makes it easier to pass a member function to an interface expecting a C-style callback (that is, the interface expects a function pointer to the callback, and a void* pointer to user data which will in turn be passed to the callback). Effectively I want to convert Helper::M to Helper::V below.
I am trying to modify the snippet to automatically deduce the template parameters. Here is my current attempt.
#include <iostream>
template <typename R, typename T, typename... Args>
struct Helper {
using V = R (*)(void*, Args...);
using M = R (T::*)(Args...);
template <M m>
static R Fn(void* data, Args... args) {
return (static_cast<T*>(data)->*m)(std::forward<Args...>(args...));
}
};
template <typename R, typename T, typename... Args>
typename Helper<R, T, Args...>::V Cast(R (T::*m)(Args...)) {
return Helper<R, T, Args...>::template Fn<m>;
}
int CIntf(void* data, int (*f)(void*, int)) { return f(data, 1); }
struct UserData {
int x;
int Add(int y) { return x + y; }
};
int main(int argv, char** argc) {
UserData data = {4};
// Explicit parameters; works.
std::cout << CIntf(&data, Helper<int, UserData, int>::Fn<&UserData::Add>)
<< "\n";
// Deduced parameters; fails.
std::cout << CIntf(&data, Cast(&UserData::Add)) << "\n";
return 0;
}
I tried to compile with gcc -std=c++11 -lstdc++. The explicit parameters method works fine, but the deduced parameters method gives the following error:
tmp.cc: In instantiation of ‘typename Helper<R, T, Args>::V Cast(R (T::*)(Args ...)) [with R = int; T = UserData; Args = {int}; typename Helper<R, T, Args>::V = int (*)(void*, int)]’:
tmp.cc:30:58: required from here
tmp.cc:15:42: error: no matches converting function ‘Fn’ to type ‘using V = int (*)(void*, int) {aka int (*)(void*, int)}’
return Helper<R, T, Args...>::template Fn<m>;
^~~~~
tmp.cc:8:12: note: candidate is: template<int (UserData::* m)(int)> static R Helper<R, T, Args>::Fn(void*, Args ...) [with R (T::* m)(Args ...) = m; R = int; T = UserData; Args = {int}]
static R Fn(void* data, Args... args) {
Note that it correctly deduced the template parameters, but failed to convert Helper<int, UserData, int>::Fn<m> to int (*)(void*, int); why? This same conversion succeeded in the explicit case (unless m is somehow different from &UserData::Add).
Unfortunately you'll have to use a macro for this:
#define makeFunc(method) &Helper<decltype(method)>::Fn<method>
And redefine your helper like this for it to work:
template <typename T>
struct Helper;
template <typename R, typename T, typename... Args>
struct Helper<R(T::*)(Args...)>
The reason why you can't use deduction for this, is that deduction only works on function arguments which are run-time values. And you need to use a method's address as template argument which should be a compile-time value.
So when you do this:
return Helper<R, T, Args...>::template Fn<m>;
you are passing a run-time value m as a template argument which is impossible.
For reference, here is the complete code using the macro. Also note the use of std::forward in the original code was incorrect for multiple arguments (see this answer).
#include <iostream>
#include <utility>
template <typename T>
struct Helper;
template <typename R, typename T, typename... Args>
struct Helper<R (T::*)(Args...)> {
template <R (T::*m)(Args...)>
static R Fn(void* t, Args... args) {
return (static_cast<T*>(t)->*m)(std::forward<Args>(args)...);
}
};
#define VOID_CAST(m) &Helper<decltype(m)>::Fn<m>
struct UserData {
int x;
int Add1(int y) { return x + y; }
int Add2(int y, int z) { return x + y + z; }
};
int Call1(void* data, int (*f)(void*, int)) { return (*f)(data, 1); }
int Call2(void* data, int (*f)(void*, int, int)) { return (*f)(data, 1, 2); }
int main() {
UserData data = {4};
std::cout << Call1(&data, VOID_CAST(&UserData::Add1)) << "\n";
std::cout << Call2(&data, VOID_CAST(&UserData::Add2)) << "\n";
return 0;
}
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.
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.
Hello I'm trying to use Boost.Test for my unit tests. However some of my functions return a collection of tuples, in a std::vector< std::tuple< TypeA, TypeB > >.
To work with BOOST_CHECK_EQUALS and BOOST_CHECK_EQUAL_COLLECTION, I specialized boost::test_tools::print_log_value to print nice tuples and vectors as the answer to this question explains. I also provide operator<< for tuples, so that my vector can use that while printing the whole vector. For cleanness this operator lies in the blank namespace.
However the compilation fails, as the implementation of boost::test_tools::print_log_value <std::vector<std::tuple<...>>> cannot find the operator<< for tuples.
Here is a minimal code which is, sorry, already quite verbose.
#define BOOST_TEST_MODULE my_test
#include <boost/test/included/unit_test.hpp>
#include <tuple>
#include <vector>
/////////////////
// std::vector //
/////////////////
// boost printing method
namespace boost {
namespace test_tools {
template< typename Type >
struct print_log_value< std::vector< Type > > {
void operator()( std::ostream& s, const std::vector< Type > &collection ) {
const int size = collection.size();
if( size == 0 ) {
s << "[]";
}
else {
s << "[ ";
for( int i =0; i <= size-2; ++i ) {
s << collection[i] << ", ";
}
s << collection[size-1] << " ]";
}
return s;
}
};
} //namespace test_tools
} //namespace boost
////////////////
// std::tuple //
////////////////
// recursive calls for printing
namespace tuple_print_aux{
template< int I, int J, typename... Types >
struct recursive_printer {
static void print( std::ostream& s, const std::tuple<Types...> &collection ) {
s << std::get< I >( collection ) << ", ";
recursive_printer< I+1, J-1, Types... >::print( s, collection );
}
};
template< int I, typename... Types >
struct recursive_printer< I, 1, Types... > {
static void print( std::ostream& s, const std::tuple<Types...> &collection ) {
s << std::get< I >( collection );
}
};
template< typename... Types >
void recursive_print( std::ostream& s, const std::tuple<Types...> &collection ) {
recursive_printer< 0, sizeof...(Types), Types... >::print( s, collection );
}
}
// output stream operator
template< typename... Types >
std::ostream& operator<<( std::ostream& s, const std::tuple<Types...> &collection ) {
s << "( ";
tuple_print_aux::recursive_print< Types... >( s, collection );
s << " )";
return s;
}
// boost printing method
namespace boost {
namespace test_tools {
template< typename... Types >
struct print_log_value< std::tuple< Types... > > {
void operator()( std::ostream& s, const std::tuple<Types...> &collection ) {
s << "( ";
tuple_print_aux::recursive_print< Types... >( s, collection );
s << " )";
}
};
} //namespace test_tools
} //namespace boost
BOOST_AUTO_TEST_CASE(my_test_case) {
//builds successfully
BOOST_CHECK_EQUAL( std::make_tuple(1,"a"), std::make_tuple(1,"a") );
//builds successfully
std::vector< int > v( 2, 3 ), w( 2, 7 );
BOOST_CHECK_EQUAL_COLLECTIONS( v.begin(), v.end(), w.begin(), w.end() );
//fails to build
std::vector< std::tuple<int,int> > a( 1, std::make_tuple(1,3) ), b( 1, std::make_tuple(2,2) );
BOOST_CHECK_EQUAL_COLLECTIONS( a.begin(), a.end(), b.begin(), b.end() );
};
Of course, putting the operator<< for std::tuples into the std namespace solve the problem, but this is a non-standard non-elegant solution.
So... how should I approach the problem. ?
Thank you for any help.
I believe you should declare
template< typename... Types >
std::ostream& operator<<( std::ostream& s, const std::tuple<Types...> &collection )
before the struct print_log_value< std::vector< Type > >. Also I think that the specialization of struct print_log_value for tuples is enough as the BOOST_CHECK_EQUAL_COLLECTIONS do not actually see the std::vector but an iterator on it.