7

I know that getters are in general bad, but here, I just use one to illustrate a more general question.

Consider the following class:

template <class... T>
class my_tuple final
{
    private: 
        std::tuple<T...> _data;
    public: 
        template <class... U> 
        my_tuple(U&&... u) 
        : _data(std::forward<U>(u)...) {} 
    public:
        template <std::size_t I>
        auto get() -> decltype(std::get<I>(_data)) 
        {return std::get<I>(_data);}
};

And consider that I cannot modify this class.

Is there a way, to write an external metafunction my_tuple_type (by external I mean a metafunction not belonging to the class) to actually get the type of the underlying tuple? (I tend to think that it is impossible if one of T... is a reference because just applying std::decay or std::remove_reference on the type returned by get will remove the original reference too).

EDIT: I have added a constructor to help testing.

EDIT2: For clarification, I cannot operate on T...: I am searching for a metafunction only based on the getter.

EDIT3: From the exterior of the class, I do not know the name of the underlying tuple member (here it is named _data, but it could be _tuple or whatever)

EDIT4: As an illustration, this can be achieved if we suppose that none of the types are references/pointers by:

1) Making a metafunction that will execute recursively the getter until it fails (so the tuple size N will be known)

2) Executing a std::decay on each type returned by std::get from 0 to N and putting them together.

But it will fail if one of the tuple element is a reference or pointer...

EDIT5: I will post an implementation of EDIT4 soon (I am working on that)

EDIT6: That is not an XY problem. The fundamental question I try to answer is:

consider a concept called Tuple_like whose only condition is to have a templated get member like here. The question is: from this only function get<I>(), is it possible to extract all information on the underlying tuple ?

Vincent
  • 57,703
  • 61
  • 205
  • 388
  • :( None of my hacks will work then. Thanks for the clarification. – dyp Feb 14 '14 at 23:51
  • @dyp Are your comments valid with the updates ? (EDIT2 and EDIT3) ? – Vincent Feb 14 '14 at 23:52
  • How do you know the *number* of elements of the tuple? Is this also something that has to be somehow extracted from the getter function? – dyp Feb 14 '14 at 23:52
  • Wait, so you want to know the type of a tuple by only examining a `size_t` and one of the types in the tuple? – user3286380 Feb 14 '14 at 23:53
  • @dyp Yes, this can been achieved easily (I think), by template recursion and specialization. – Vincent Feb 14 '14 at 23:53
  • Then I don't understand your constraints. You cannot know the type of a tuple based on its minimum size and the type of one of the members it has, which is all the info available by looking at the declaration of `get`. If you knew the actual size it might help. – user3286380 Feb 14 '14 at 23:54
  • Actually I take that back, with SFINAE it might be possible. – user3286380 Feb 14 '14 at 23:56
  • @Vincent Not sure what you have in mind. If you can only use the getter to .. get information about the tuple, you'll always have to specify an index of which element to get. The only way that comes to my mind is to use SFINAE and rely on the ill-formedness of the program if the index is out of range. – dyp Feb 14 '14 at 23:57
  • @dyp is ill-formedness of an out-of-bounds tuple access not reliable to work with SFINAE to determine the max index of a tuple? – user3286380 Feb 14 '14 at 23:59
  • So.. you already have an implementation. Why not post it as an answer? ;) – dyp Feb 14 '14 at 23:59
  • @user3286380 I'm not if sure what you're asking is the same thing I suggested.. The Standard says in [tuple.elem] "The program is ill-formed if `I` is out of bounds." where `I` is the index used. The implementation could accept the program despite being ill-formed, I'd be glad but am not sure if the SFINAE behaviour is defined for such cases. See the discussion [here](http://stackoverflow.com/questions/21502017/is-the-compiler-allowed-leeway-in-what-it-considers-undefined-behavior-in-a-cons#comment32522870_21502017) – dyp Feb 15 '14 at 00:03
  • 2
    Could you expand on EDIT2? I'm not yet convinced this isn't an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). It seems strange you may rely on `get()` but not on the actual type `my_tuple`. – Cornstalks Feb 15 '14 at 00:04
  • @dyp yeah that's what I was asking. – user3286380 Feb 15 '14 at 00:05
  • @Vincent Not only is there a problem because the reference is added no matter if the tuple element is a reference or not; whether the return type is an lvalue or rvalue reference also depends on the value category of the tuple argument of `get`. I.e. you'll never get an rvalue reference return value for an lvalue tuple argument. – dyp Feb 15 '14 at 00:06
  • @Cornstalks see EDIT6 – Vincent Feb 15 '14 at 00:10
  • @Vincent: so you have multiple different classes, the type of which may vary (for example, one may not even be templated), but all of these classes implement some `get()` method, and you need to be able to call this `get()` method from some generic piece of code? Am I summing this up correctly? – Cornstalks Feb 15 '14 at 00:14
  • @Cornstalks: the first part of the sentence is ok (until and), and then you can replace it by "and you want to know if there a way to treat them as standard tuples in a generic way." (and I will be able to do that only if I can implement a `std::tuple_size` and a `std::tuple_element` from the getter) – Vincent Feb 15 '14 at 00:19
  • @Vincent Can we somehow get two ref-qualified getters `something get() &;` and `something get() &&;`? It should be possible to identify lvalue-references then (albeit not rvalue-refs). Adding const-qualified getters should allow detecting *all* references. – dyp Feb 15 '14 at 00:22
  • Without speaking about the reference type problem, it does not seem obvious to me that you can actually compute the size of the tuple in this condition. Using SFINAE, what would be your non-failing specialization ? – log0 Feb 15 '14 at 00:28
  • Re edit6: how do you know there is an underlying tuple, without knowing its name? `get`s interface does not imply one exists. – Yakk - Adam Nevraumont Feb 15 '14 at 00:59
  • I don't think it's possible. `std::get<0, int>` has the exact same return type as `std::get<0, int&>` (`int&`). So `my_tuple::get<0>` will have the same signature as `my_tuple::get<0>`. – zch Feb 15 '14 at 01:04

1 Answers1

2

No, std::get<I>(some_tuple&) is lossy. It returns the same type for references and value types. If there was a way to have an rvalue qualified call to get you could do it.

Well, there is pattern matching on the my_tuple type itself. And if you knew the name of the std::tuple field (or had a list of all possible names even) there are ways to violate privacy, which might work here. But I suspect those are excluded.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Is an rvalue-ref-qualified `get` really sufficient? You can use it to discriminate between lvalue-refs and {rvalue-refs, non-refs}, but can you also use it to discriminate between rvalue-refs and non-refs? I thought an additional `const`-qualified version would be required to do that.. – dyp Feb 15 '14 at 12:54
  • @dyp Not sure: I have not used many `std::tuple`: but an rvalue reference to `this` based `get` will be far more obscure than a `const` reference to `this` method anyhow. And as I read the question, both are ruled out (I just mentioned them in the off chance we had an X/Y). – Yakk - Adam Nevraumont Feb 15 '14 at 13:29
  • It is possible that getting the size of the tuple via SFINAE is impossible, as the error might not happen in the *immediate context*. At least, that's what my tests indicate. – dyp Feb 15 '14 at 13:54
  • [This is what I have so far.](http://coliru.stacked-crooked.com/a/08631fab1895f16e) It still has the *size* problem, and doesn't yet discriminate between rvalue-refs and const-qualified non-refs. – dyp Feb 15 '14 at 14:02