Is uniform dereferencing syntax possible? - c++

Say I have a type with a function f():
struct A { void f() {} };
and two vectors:
std::vector<A*> x;
std::vector<A*******> y;
(The silly number of pointers is just for dramatic effect.)
I'm looking for a way to be able to write:
deep_deref(std::begin(x)).f();
deep_deref(std::begin(y)).f();
In other words, what I want is a uniform dereferencing syntax powered by a universal, multi-level, smart dereferencing function (or something else that'll allow uniform dereferencing syntax) deep_deref() that will dereference the object passed to it, then the object obtained from that dereference, then the next, and so on until it arrives at a non-dereferencable object, at which point it'll return the final object.
Note that along this path of dereferencing there may lie all kinds of dereferencable objects: pointers, iterators, smart pointers, etc — anything dereferencable.
Is something like this possible? (Assume I have is_dereferencable.)

Using std::decay to create a can_dereference function by dereferencing and removing CV qualifiers, this can be done.
The answer linked to here is a full implementation along with a live sample.
I originally posted this as a comment but thought an answer would be better to help people searching

template < typename T, bool isDeref = is_dereferenceable<T>::value >
struct deref_
{
static T& call(T & t) { return t; }
};
template < typename T >
struct deref_<T,true>
{
static decltype(*declval<T>()) call(T & t) { return deref_<decltype(*t)>::call(*t); }
};
template < typename T >
auto deref(T & t) { return deref_<T>::call(t); }
This is untested and incomplete. You should be using && and forward and all that. Much cleanup and re-ordering to do...
I also question the wisdom of having something like this around.

Some boilerplate:
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class... Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
templateclass Z, class... Ts>
using can_apply = details::can_apply;
A trait to determine if you should *:
template<class T>
using unary_star_r = decltype( *std::declval<T>() );
template<class T>
using can_unary_star = can_apply<unary_star_r, T>;
dispatch takes two arguments and picks between them at compile time:
template<bool /*false*/>
struct dispatch_t {
template<class T, class F>
F operator()(T, F f)const{ return std::move(f); }
};
template<>
struct dispatch_t<true> {
template<class T, class F>
T operator()(T t, F)const{ return std::move(t); }
};
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__))\
->decltype(__VA_ARGS__)\
{ return __VA_ARGS__; }
template<bool b, class T, class F>
auto
dispatch( T t, F f )
RETURNS( dispatch_t<b>{}( std::move(t), std::move(f) ) )
We are almost done...
Now our work. We write function objects that represent dereferencing a type, doing nothing, and maybe doing either:
struct maybe_deref_t;
struct do_deref_t;
struct identity_t {
template<class T>
T operator()(T&& t)const { return std::forward<T>(t); }
};
struct do_deref_t {
template<class T>
auto operator()(T&& t)const
RETURNS( maybe_deref_t{}( *std::forward<T>(t) ) )
};
Here is the work:
struct maybe_deref_t {
template<class T>
auto operator()(T&& t)const
RETURNS(
dispatch< can_unary_star<T>::value >(
do_deref_t{},
identity_t{}
)(
std::forward<T>(t)
)
)
};
and a helper for better syntax:
template<class T>
auto maybe_deref( T&& t )
RETURNS( maybe_deref_t{}( std::forward<T>(t) ) )
Test code:
int main() {
auto bob = new int*( new int(7) ); // or 0 or whatever
std::cout << maybe_deref(bob) << "\n";
}
live example.
I originally wrote this in a C++14 style, then back-translated it to C++11. In C++14 it is much cleaner.

For anybody who can use a more recent version of C++:
#include <utility>
namespace detail
{
struct Rank_0 {};
struct Rank_1 : Rank_0{}; // disambiguate overloads
template <class T, std::void_t<decltype(*std::declval<T>())>* = nullptr>
decltype(auto) deep_deref_impl(T& obj, Rank_1)
{
return deep_deref_impl(*obj, Rank_1{});
}
template <class T>
decltype(auto) deep_deref_impl(T& obj, Rank_0)
{
return obj;
}
}
template <class T>
decltype(auto) deep_deref(T& obj)
{
return detail::deep_deref_impl(obj, detail::Rank_1{});
}
auto test()
{
int a = 24;
int* p1 = &a;
int** p2 = &p1;
int*** p3 = &p2;
int**** p4 = &p3;
deep_deref(a) += 5;
deep_deref(p4) += 11;
return a; // 40
}
Check it at godbolt

I like to keep things simple... I'd implement it like this:
template <class T>
auto deep_deref_impl(T&& t, int) -> decltype(deep_deref_impl(*t, int{})) {
return deep_deref_impl(*t, int{});
}
template <class T>
T &deep_deref_impl(T&& t, ...) {
return t;
}
template <class T>
auto deep_deref(T&& t) -> decltype(deep_deref_impl(std::forward<T>(t), int{})) {
return deep_deref_impl(std::forward<T>(t), int{});
}
[live demo]

Related

how to forward the types of tuple to specialize other template?

currently I'm working on a dynamic container structure, which represents one pod value or has vector of pointers with same container type. The container has an interface optional<T> expect_value<T>() 。 For pod types the implemention is simple. For the non pod value, I would call expect_value<tuple<args...>>(), the args would be tuple as well. But when implement this function, I come across a trouble: how to redirect a.expect_value<tuple<args...>>() to a.expect_value_tuple<args...>>(). For example, the call to a.expect_value<tuple<int,int>() would return the result of a.expect_value_tuple<int, int>(). Because the argument is empty, I cant use the type deduce of unpacked arguments. Then the whole project just cant progress any more. Any ideas? Below is the minimal example for my intention.
#include <tuple>
#include <vector>
#include <optional>
#include <functional>
using namespace std;
template<typename T>
struct is_tuple_impl : std::false_type {};
template<typename... Ts>
struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
template<typename T>
struct is_tuple : is_tuple_impl<std::decay_t<T>> {};
class my_container;
template<typename... args, size_t... arg_idx>
optional<tuple<args>...> get_tuple_value_from_vector(const vector<my_container*>& v_list, std::index_sequence<arg_idx...>)
{
auto temp_result = make_tuple((*v_list[arg_idx]).expect_value<arg>()...);
if(!(get<arg_idx>(temp_result) &&...))
{
return nullopt;
}
return make_tuple(get<arg_idx>(temp_result).value()...);
}
class my_container
{
public:
int value_type; // 1 for v_int 2 for v_list 0 empty
union
{
int v_int;
};
vector<my_container*> v_list;
template<typename T>
optional<T> expect_simple_value();
template<typename... args>
optional<tuple<args...>> expect_tuple_value();
template<typename T>
optional<T> expect_value();
};
template <typename T>
optional<T> my_container::expect_simple_value()
{
return nullopt;
}
template <>
optional<int> my_container::expect_simple_value()
{
if(value_type == 1)
{
return v_int;
}
return nullopt;
}
template<typename... args>
optional<tuple<args...>> my_container::expect_tuple_value()
{
if(v_list.size() == 0)
{
return nullopt;
}
for(const auto i: v_list)
{
if(!i)
{
return nullopt;
}
}
auto the_tuple_size = sizeof...(args);
if(v_list.size() != the_tuple_size)
{
return nullopt;
}
return get_tuple_value_from_vector<args...>(v_list, index_sequence_for<args...>{});
}
template <typename T>
optional<T> my_container::expect_value()
{
if(is_tuple<T>::value)
{
return expect_tuple_value<T>();
}
else
{
return expect_simple_value<T>();
}
}
int main()
{
my_container test_value;
test_value.value_type = 1;
test_value.v_int = 1;
auto result = test_value.expect_value<tuple<int, int>>();
if(result)
{
return 0;
}
else
{
return 1;
}
}
the heart of the problem is the line return expect_tuple_value<T>(); When logic goes there, the T should be tuple<args...>, but what I want is return return expect_tuple_value<args...>().
What about using template argument deduction and overload resolution through partial ordering of function template:
class my_container
{
public:
template<class T> optional<T> expect_value_simple();
template<class...Args> optional<tuple<Args...>> expect_value_tuple();
private:
template<class T> struct deduce_type{};
template<typename T>
auto expect_value_dispatching(deduce_type<T>){
return expect_value_simple<T>();
}
template<typename...Args>
auto expect_value_dispatching(deduce_type<tuple<Args...>>){
return expect_value_tuple<Args...>();
}
public:
template<typename T>
auto expect_value(){
return expect_value_dispatching(deduce_type<T>{});
}
};
(Demo)
The if before the line in question should be a constexpr if.
Unpacking of types is annoying to do without using a class helper. I can do it with some fancy c++14 lambda action tho.
template<class T>
struct tag_t{using type=T;};
template<class Tag>
using type=typename Tag::type;
template<class Tuple>
struct unpack_tuple;
template<class...Ts>
struct unpack_tuple<std::tuple<Ts...>> {
template<class F>
decltype(auto) operator()(F&& f)const {
return std::forward<F>(f)( tag_t<Ts>{}... );
}
};
#define TYPE_FROM(...) \
type< std::decay_t<decltype(__VA_ARGS__)> >
then we get
if constexpr(is_tuple<T>::value)
{
return unpack_tuple<T>{}([&](auto...tags){
return expect_tuple_value<TYPE_FROM(tags)...>();
});
}
else
{
return expect_simple_value<T>();
}
and done.
The core issue here is that you need to do argument deduction at least once to go back from a type to its variadic parameters. To do that, you must pass some instance of such a variadically-templated type to a function - but it does not have to be the original one.
Yakk's solution does this via a variadic lambda that is passed instances of tag-types (one per tuple type). The advantage here is that you can use a lambda instead of an explicit intermediary function every time.
Oliv's solution uses a monostate type which we can instantiate and pass to a function for type deduction. It's much cleaner but requires such an intermediary function for every use case.
Here is a (more or less theoretical) version combining both, using templated variadic lambdas (C++20, and they apparently don't even have clang support as of now):
template<class... Args>
struct MonostateTuple
{};
template<class... Args>
auto tupleToMonostate(std::tuple<Args...>)
{
return MonostateTuple<Args...>{};
}
template<class T, class F>
auto unpack_tuple(F&& f)
{
using MT = decltype(tupleToMonostate(std::declval<T>()));
return std::forward<F>(f)(MT{});
}
/// User code
template<class Tuple>
auto foo()
{
return unpack_tuple<Tuple>([&] <typename... Args> (MonostateTuple<Args...>) {
return expect_tuple_value<Args...>();
});
}
It's a bit more ugly in the lambda signature (not to mention the lack of compiler support again) but theoretically combines both advantages.

Variadic sequence of pointer to recursive member of struct/class as template parameter

I'm struggling with some template programming and I hope you can give me some help. I coded a C++11 interface that, given some structs like:
struct Inner{
double a;
};
struct Outer{
double x, y, z, r;
Inner in;
};
Implements a getter/setter to the real data that is customized to the specified struct members:
MyData<Outer, double, &Outer::x,
&Outer::y,
&Outer::z,
&Outer::in::a //This one is not working
> state();
Outer foo = state.get();
//...
state.set(foo);
I managed to implement this for simple structs in the following way:
template <typename T, typename U, U T::* ... Ms>
class MyData{
std::vector<U *> var;
public:
explicit MyData();
void set(T const& var_);
T get() const;
};
template <typename T, typename U, U T::* ... Ms>
MyData<T, U, Ms ... >::Struct():var(sizeof...(Ms))
{
}
template <typename T, typename U, U T::* ... Ms>
void MyData<T, U, Ms ...>::set(T const& var_){
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
*var[i++] = var_.*d;
}
}
template <typename T, typename U, U T::* ... Ms>
T MyData<T, U, Ms ...>::get() const{
T var_;
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
var_.*d = *var[i++];
}
return var_;
}
But it fails when I pass a member of a nested struct. Ideally, I'd like to implement a generic pointer to member type that allows me to be compatible with several levels of scope resolutions. I found this approach, but I'm not sure if this should be applied to my problem or if there exists some implementation ready to use. Thanks in advance!
Related posts:
Implicit template parameters
Pointer to inner struct
You might wrap member pointer into struct to allow easier chaining:
template <typename...> struct Accessor;
template <typename T, typename C, T (C::*m)>
struct Accessor<std::integral_constant<T (C::*), m>>
{
const T& get(const C& c) { return c.*m; }
T& get(C& c) { return c.*m; }
};
template <typename T, typename C, T (C::*m), typename ...Ts>
struct Accessor<std::integral_constant<T (C::*), m>, Ts...>
{
auto get(const C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
auto get(C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
};
template <typename T, typename U, typename ...Ts>
class MyData
{
std::vector<U> vars{sizeof...(Ts)};
template <std::size_t ... Is>
T get(std::index_sequence<Is...>) const
{
T res;
((Ts{}.get(res) = vars[Is]), ...); // Fold expression C++17
return res;
}
template <std::size_t ... Is>
void set(std::index_sequence<Is...>, T const& t)
{
((vars[Is] = Ts{}.get(t)), ...); // Fold expression C++17
}
public:
MyData() = default;
T get() const { return get(std::index_sequence_for<Ts...>()); }
void set(const T& t) { return set(std::index_sequence_for<Ts...>(), t); }
};
With usage similar to
template <auto ...ms> // C++17 too
using Member = Accessor<std::integral_constant<decltype(ms), ms>...>;
MyData<Outer, double, Member<&Outer::x>,
Member<&Outer::y>,
Member<&Outer::z>,
Member<&Outer::in, &Inner::a>
> state;
std::index_sequence is C++14 but can be implemented in C++11.
Folding expression from C++17 can be simulated too in C++11.
typename <auto> (C++17) should be replaced by typename <typename T, T value>.
Demo
A generalization of a member pointer is a function that can map T to X& at compile time.
In c++17 it isn't hard to wire things up thanks to auto. In c++11 it gets harder. But the basic idea is that you don't actually pass member pointers, you pass types, and those types know how to take your class and get a reference out of them.
template<class T, class D, class...Fs>
struct MyData {
std::array<D*, sizeof...(Fs)> var = {};
explicit MyData()=default;
void set(T const& var_) {
var = {{ Fs{}(std::addressof(var_))... }};
}
T get() {
T var_;
std::size_t index = 0;
using discard=int[];
(void)discard{ 0, (void(
*Fs{}(std::addressof(var_)) = *var[index++]
),0)... };
return var_;
}
};
it remains to write a utility that makes writing the Fs... easy for the member pointer case
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M > {
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
#define TYPE_N_VAL(...) \
decltype(__VA_ARGS__), __VA_ARGS__
#define MEM_PTR(...) get_ptr_to_member_t< TYPE_N_VAL(__VA_ARGS__) >
now the basic case is
MyData< Outer, double, MEM_PTR(&Outer::x), MEM_PTR(&Outer::y) >
The more complex case can now be handled.
An approach would be to teach get_ptr_to_member to compose. This is annoying work, but nothing fundamental. Arrange is so that decltype(ptr_to_member_t * ptr_to_member_t) returns a type that instances right, applies it, then takes that pointer and runs the left hand side on it.
template<class First, class Second>
struct composed;
template<class D>
struct composes {};
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<class First, class Second>
struct composed:composes<composed<First, Second>> {
template<class In>
auto operator()(In&& in) const
RETURNS( Second{}( First{}( std::forward<In>(in) ) ) )
};
template<class First, class Second>
composed<First, Second> operator*( composes<Second> const&, composes<First> const& ) {
return {};
}
then we upgrade:
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M >:
composes<get_ptr_to_member_t< D T::*, M >>
{
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
and now * composes them.
MyData<TestStruct, double, MEM_PTR(&Outer::x),
MEM_PTR(&Outer::y),
MEM_PTR(&Outer::z),
decltype(MEM_PTR(&Inner::a){} * MEM_PTR(&Outer::in){})
> state();
answre probably contains many typos, but design is sound.
In c++17 most of the garbage evaporates, like the macros.
I would use lambda approach to implement similar functionalities in C++17(C++14 is also ok, just change the fold expression):
auto access_by() {
return [] (auto &&t) -> decltype(auto) {
return decltype(t)(t);
};
}
template<class Ptr0, class... Ptrs>
auto access_by(Ptr0 ptr0, Ptrs... ptrs) {
return [=] (auto &&t) -> decltype(auto) {
return access_by(ptrs...)(decltype(t)(t).*ptr0);
};
}
auto data_assigner_from = [] (auto... accessors) {
return [=] (auto... data) {
return [accessors..., data...] (auto &&t) {
((accessors(decltype(t)(t)) = data), ...);
};
};
};
Let's see how to use these lambdas:
struct A {
int x, y;
};
struct B {
A a;
int z;
};
access_by function can be used like:
auto bax_accessor = access_by(&B::a, &A::x);
auto bz_accessor = access_by(&B::z);
Then for B b;, bax_accessor(b) is b.a.x; bz_accessor(b) is b.z. Value category is also preserved, so you can assign: bax_accessor(b) = 4.
data_assigner_from() will construct an assigner to assign a B instance with given accessors:
auto data_assigner = data_assigner_from(
access_by(&B::a, &A::x),
access_by(&B::z)
);
data_assigner(12, 3)(b);
assert(b.z == 3 && b.a.x == 12);

Remove last item from function parameter pack

I am writing a method to extract values from arbitrarily nested structs. I am almost there, but would like to also provide an option to convert the value retrieved (by default no conversion). Since parameter packs can't be followed by another template parameter, I have to fudge this a bit. The below works except for the indicated line:
#include <iostream>
#include <type_traits>
typedef struct {
int a;
int b;
} bar;
typedef struct {
int c;
bar d;
} baz;
template <typename T, typename S, typename... Ss>
auto inline getField(const T& obj, S field1, Ss... fields)
{
if constexpr (!sizeof...(fields))
return obj.*field1;
else
return getField(obj.*field1, fields...);
}
template <typename Obj, typename Out, class ...C, typename... T>
auto inline getFieldC(const Obj& obj, Out, T C::*... field)
{
return static_cast<Out>(getField(obj, field...));
}
template<class T> struct tag_t { using type = T; };
template<class...Ts>
using last = typename std::tuple_element_t< sizeof...(Ts) - 1, std::tuple<tag_t<Ts>...> >::type;
template <typename Obj, typename... T>
auto getMyFieldWrapper(const Obj& obj, T... field)
{
if constexpr (std::is_member_object_pointer_v<last<Obj, T...>>)
return getField(obj, field...);
else
return getFieldC(obj, last<Obj, T...>{}, field...); // <- this doesn't compile, need a way to pass all but last element of field
}
int main()
{
baz myObj;
std::cout << getMyFieldWrapper(myObj, &baz::c); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b, 0.); // doesn't work
}
How do I implement the indicated line? I'm using the latest MSVC, and am happy to make full use of C++17 to keep things short and simple.
Usually more helpful to invert the flow. First, write a higher-order function that forwards an index sequence:
template <typename F, size_t... Is>
auto indices_impl(F f, std::index_sequence<Is...>) {
return f(std::integral_constant<size_t, Is>()...);
}
template <size_t N, typename F>
auto indices(F f) {
return indices_impl(f, std::make_index_sequence<N>());
}
That is just generally useful in lots of places.
In this case, we use it to write a higher-order function to drop the last element in a pack:
template <typename F, typename... Ts>
auto drop_last(F f, Ts... ts) {
return indices<sizeof...(Ts)-1>([&](auto... Is){
auto tuple = std::make_tuple(ts...);
return f(std::get<Is>(tuple)...);
});
}
And then you can use that:
return drop_last([&](auto... elems){
return getMyField(obj, last<Obj, T...>{}, elems...);
}, field...);
References omitted for brevity.
Of course, if you want to combine both and just rotate, you can do:
// Given f and some args t0, t1, ..., tn, calls f(tn, t0, t1, ..., tn-1)
template <typename F, typename... Ts>
auto rotate_right(F f, Ts... ts) {
auto tuple = std::make_tuple(ts...);
return indices<sizeof...(Ts)-1>([&](auto... Is){
return f(
std::get<sizeof...(Ts)-1>(tuple),
std::get<Is>(tuple)...);
});
}
used as:
return rotate_right([&](auto... elems){
return getMyField(obj, elems...);
}, field...);
How do I implement the indicated line?
Not sure to understand what do you want but... it seems to me that you can make it calling an intermediate function
template <std::size_t ... Is, typename ... Ts>
auto noLastArg (std::index_sequence<Is...> const &,
std::tuple<Ts...> const & tpl)
{ return getMyField(std::get<Is>(tpl)...); }
you can rewrite your function as follows
template <typename Obj, typename ... T>
auto getMyFieldWrapper (Obj const & obj, T ... field)
{
if constexpr (std::is_member_object_pointer<last<Obj, T...>>::value )
return getMyField(obj, field...);
else
return noLastArg(std::make_index_sequence<sizeof...(T)>{},
std::make_tuple(obj, field...));
}
The idea is pack the arguments for getMyField in a std::tuple of sizeof...(T)+1u elements (+1 because there is also obj) and call getMyField() unpacking the first sizeof...(T) of them.
But isn't clear, to me, if you want also last<Obj, T...>{}.
In this case, the call to noLastArg() become
return noLastArg(std::make_index_sequence<sizeof...(T)+1u>{},
std::make_tuple(obj, last<Obj, T...>{}, field...));

Make struct behave like std::tuple

I've written some generic code which manages a list of tuples. Now I want to use that code, but instead of std::tuple I would like to use simple structs, so I can access the variables using names instead of indicies. Is there an easy way to make these structs behave like std::tuple, so I can use it with my generic code?
struct foo {
int x;
float y;
// some code to enable tuple like behavior (e.g. std::get, std::tuple_size)
};
I've tried adding a as_tuple member function which returns all members using std::tie. This works but requires to call this member function at all places where I need the tuple behavior.
The manual way:
struct foo {
int x;
float y;
};
namespace std
{
template <>
class tuple_element<0, foo> {
using type = int;
};
template <>
class tuple_element<1, foo> {
using type = float;
};
template <std::size_t I>
tuple_element_t<I, foo>& get(foo&);
template <>
tuple_element_t<0, foo>& get(foo& f) { return f.x;}
template <>
tuple_element_t<1, foo>& get(foo& f) { return f.y; }
template <std::size_t I>
tuple_element_t<I, foo> get(const foo&);
template <>
tuple_element_t<0, foo> get(const foo& f) { return f.x;}
template <>
tuple_element_t<1, foo> get(const foo& f) { return f.y; }
}
An other way is to write functions as_tuple:
template <typename ... Ts>
std::tuple<Ts...>& as_tuple(std::tuple<Ts...>& tuple) { return tuple; }
std::tuple<int&, float&> as_tuple(foo& f) { return std::tie(f.x, f.y); }
and wrap your call before using tuple-like.
First, as_tuple should be a free function in the namespace of the class. This lets you extend types other people write.
Next, you should attempt to call get in an ADL-enabled context.
using std::get;
auto& x = get<1>(foo);
if you do that we can pull off some magic.
struct get_from_as_tuple {
template<std::size_t I,
class T,
std::enable_if_t< std::is_base_of< get_from_as_tuple, std::decay_t<T> >, bool > = true
>
friend decltype(auto) get( T&& t ) {
return std::get<I>( as_tuple( std::forward<T>(t) ) );
}
};
now
struct foo:get_from_as_tuple {
int x;
float y;
friend auto as_tuple( get_from_as_tuple const& self ) {
return std::tie( self.x, self.y );
}
};
we can do this:
foo f;
using std::get;
std::cout << get<0>(f) << "," << get<1>(f) << "\n";
Now, this still doesn't enable tuple_size and tuple_element.
There is no trivial way to do that part, but we can work around it.
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
namespace tup {
namespace adl_get {
using std::get;
template<std::size_t I,
class T
>
auto get_helper( T&& t )
RETURNS( get<I>(std::forward<T>(t) ) )
}
template<std::size_t I, class T>
auto get( T&& t )
RETURNS(adl_get::get_helper<I>(std::forward<T>(t)))
}
now tup::get<7>( x ) will dispatch to either std::get or another get in x's namespace based off of overload resolution rules.
We can create similar helpers:
namespace util {
template<class T>
struct tag_t {constexpr tag_t(){}};
template<class T>
constexpr tag_t<T> tag{};
}
namespace tup {
namespace adl_tuple_size {
template<class T>
constexpr std::size_t get_tuple_size( tag_t<T>, ... ) {
return std::tuple_size<T>::value;
}
template<class T>
constexpr auto get_tuple_size( tag_t<T>, int )
RETURNS( tuple_size( tag_t<T> ) )
}
template<class T>
constexpr std::size_t tuple_size() {
return adl_tuple_size::get_tuple_size( tag<T> );
}
}
now tup::tuple_size<Foo>() is a constexpr call that gets the size of Foo by either (A) invoking tuple_size( tag_t<Foo> ) in an ADL-enabled context, or (B) returning std::tuple_size<Foo>::value.
Once we have this we can create another helper base type:
struct tuple_size_from_as_tuple {
template<std::size_t I,
class T,
std::enable_if_t< std::is_base_of< get_from_as_tuple, std::decay_t<T> >, bool > = true
>
friend std::size_t tuple_size( T&& t ) {
return std::tuple_size< decltype(as_tuple( std::forward<T>(t) ) ) >::value;
}
};
struct as_tuple_helpers : get_from_as_tuple, tuple_size_from_as_tuple {};
struct foo:as_tuple_helpers {
// ..
};
and we now have 2 primitives.
Repeat this for tag_t<E&> tuple_element( tag_t<T> ). Then we can write a tup::tuple_element<T, 0> alias that dispatches as you like it.
Finally, adapt your existing code that works with std:: tuple facilities to use tup:: facilities. It should work with existing tuple code, and will also work with types inherited from as_tuple_helper which has a friend as_tuple defined.
This does not, however, give you support for structured bindings.

Add operator[] for vector when Type is unique_ptr

Suppose that we have the vector class below which has been shortened to minimum to showcase the question.
template <typename T>
class VectorT : private std::vector<T>
{
using vec = std::vector<T>;
public:
using vec::operator[];
using vec::push_back;
using vec::at;
using vec::emplace_back;
// not sure if this is the beast way to check if my T is really a unique_ptr
template<typename Q = T>
typename Q::element_type* operator[](const size_t _Pos) const { return at(_Pos).get(); }
};
Is there any way to check if T is a unique_ptr and if yes to add an operator[] to return the unique_ptr::element_type*. At the same time though the normal operator[] should also work.
VectorT<std::unique_ptr<int>> uptr_v;
uptr_v.emplace_back(make_unique<int>(1));
//int* p1 = uptr_v[0]; // works fine if using vec::operator[]; is commented out
// then of course it wont work for the normal case
//std::cout << *p1;
VectorT<int*> v;
v.emplace_back(uptr_v[0].get());
int *p2 = v[0];
std::cout << *p2;
Any way to achieve something like that ?
Edited:
The reason I am asking for this is cause I can have say my container
class MyVec: public VectorT<std::unique_ptr<SomeClass>>
but I can also have a
class MyVecView: public VectorT<SomeClass*>
Both classes will pretty much have identical functions. So I am trying to avoid duplication by doing something like
template<typename T>
void doSomething(VectorT<T>& vec)
{
SomeClass* tmp = nullptr;
for (size_t i = 0; i < vec.size(); ++i)
{
tmp = vec[i]; // this has to work though
....
}
}
Then of course I can
MyVec::doSomething(){doSomething(*this);}
MyVecView::doSomething(){doSomething(*this);}
which of course means that the operator[] has to work for both cases
The goal here is to have only one operator[]. Techniques with more than one operator[] violate DRY (don't repeat yourself), and it is hard to avoid having a template method whose body would not compile if instantiated (which, under a strict reading of the standard, could result in your code being ill-formed).
So what I'd do is model the "turn something into a pointer" like this:
namespace details {
template<class T>
struct plain_ptr_t;
//specialzation for T*
template<class T>
struct plain_ptr_t<T*> {
T* operator()(T* t)const{return t;}
};
//specialzation for std::unique_ptr
template<class T, class D>
struct plain_ptr_t<std::unique_ptr<T,D>> {
T* operator()(std::unique_ptr<T>const& t)const{return t.get();}
};
//specialzation for std::shared_ptr
template<class T>
struct plain_ptr_t<std::shared_ptr<T>> {
T* operator()(std::shared_ptr<T>const& t)const{return t.get();}
};
}
struct plain_ptr {
template<class T>
typename std::result_of< details::plain_ptr_t<T>( T const& ) >::type
operator()( T const& t ) const {
return details::plain_ptr_t<T>{}( t );
}
};
now plain_ptr is a functor that maps smart pointers to plain pointers, and pointers to pointers.
It rejects things that aren't pointers. You could change it to just pass them through if you like, but it takes a bit of care.
We then use them to improve your operator[]:
typename std::result_of< plain_ptr(typename vec::value_type const&)>::type
operator[](size_t pos) const {
return plain_ptr{}(at(pos));
}
notice that it is no longer a template.
live example.
template<typename T> struct unique_ptr_type { };
template<typename T> struct unique_ptr_type<std::unique_ptr<T>> { using type = T; };
namespace detail {
template<typename T> std::false_type is_unique_ptr(T const&);
template<typename T> std::true_type is_unique_ptr(std::unique_ptr<T> const&);
}
template<typename T>
using is_unique_ptr = decltype(detail::is_unique_ptr(std::declval<T>()));
template<typename T>
class VectorT : std::vector<T> {
using vec = std::vector<T>;
public:
using vec::at;
using vec::emplace_back;
using vec::push_back;
template<typename Q = T,
typename std::enable_if<!is_unique_ptr<Q>::value>::type* = nullptr>
Q& operator [](std::size_t pos) { return vec::operator[](pos); }
template<typename Q = T,
typename std::enable_if<!is_unique_ptr<Q>::value>::type* = nullptr>
Q const& operator [](std::size_t pos) const { return vec::operator[](pos); }
template<typename Q = T,
typename U = typename unique_ptr_type<Q>::type>
U* operator [](std::size_t pos) { return vec::operator[](pos).get(); }
template<typename Q = T,
typename U = typename unique_ptr_type<Q>::type>
U const* operator [](std::size_t pos) const { return vec::operator[](pos).get(); }
};
Online Demo
SFINAE is used to only enable the custom operator[] if T is std::unique_ptr<T>, and to only enable std::vector<T>::operator[] otherwise.
If you insist on plain pointers you could write something like
template<class T> auto plain_ptr(T* p) { return p; }
template<class T> auto plain_ptr(std::unique_ptr<T>& p) { return p.get(); }
and then do
tmp = plain_ptr(vec[i]); // tmp will be SomeClass*
or you could have tmp local:
template<typename T>
void doSomething(VectorT<T>& vec)
{
static_assert(std::is_same<T, SomeClass*>::value ||
std::is_same<T, std::unique_ptr<SomeClass>>::value,
"doSomething expects vectors containing SomeClass!");
for (std::size_t i = 0; i < vec.size(); ++i)
{
auto tmp = vec[i];
// ... (*tmp).foo or tmp->foo ...
}
// or perhaps even
for(auto&& tmp : vec)
{
// ... again (*tmp).foo or tmp->foo ...
}
}