2

Sorry I didn't really know how to call my question, hope it fits...

I have a function template which gets an argument as template parameter. For that argument I need to have another template parameter which declares the type of the argument but while calling that function later on, I would like to omit the type of the argument. So, I would like to have some kind of typedef (or other mechanism) to get rid of it.

I've seen a similar mechnism with other templates, e.g.

// given: rule is a template with three arguments
template<typename Attr> using Rule = rule<It, Attr(), Skipper>;

When using std::get one can get along without mentioning the enum class directly:

std::get<static_cast<std::size_t>(Enum::type1)>(tuple);

Here is the function, it is used to access a tuple with an enum (compare: https://stackoverflow.com/a/14835597/2524462)

template<typename Enum, Enum enum, class ... Types>
typename std::tuple_element<static_cast<std::size_t>(enum), std::tuple<Types...> >::type&
get(std::tuple<Types...>& t) {
  return std::get<static_cast<std::size_t>(enum)>(t);
}

Since it will be used with several enums, I don't want to hardwire the enum in the template like he did.

It is called like: (1)

std::cout << get<myEnum, myEnum::type1>(tuple);

Questions:

  • Can I use a typedef or similar to call it just like:

    std::cout << new_get < myEnum::type1 > (tuple);
    
  • Since it works with std::get, is there a way to have a smarter template, in the first place?

  • The get template here has the tuple types as last parameters. Why is it not necessary to spell them out here (1)? How does the compiler figure them out from the given parameter?

I'm using gcc 4.8.1 with C++11 enabled.

Community
  • 1
  • 1
Mike M
  • 2,263
  • 3
  • 17
  • 31
  • Use default arguments maybe? – Kerrek SB Jul 19 '13 at 14:01
  • 1
    Could this question be simplified? Could you just say what you currently have (working) and how you would like it to be? – Kerrek SB Jul 19 '13 at 14:12
  • The template like stated here works. It is called like in (1), which I find too verbose. I would like to call it like spelled out in my first question. – Mike M Jul 19 '13 at 14:16
  • This isn't possible, for similar reasons to this: http://stackoverflow.com/q/11745764/951890 – Vaughn Cato Jul 19 '13 at 14:16
  • @VaughnCato: when using: "std::get(Enum::type1)>(tuple);" It works without spelling out the enum directly, so there is a way... – Mike M Jul 19 '13 at 14:19
  • I don't understand your logic. As I understand it, you are wanting to achieve a particular syntax. Just because you can get a certain result using one syntax doesn't imply you can get that same result using a different syntax. – Vaughn Cato Jul 19 '13 at 14:23
  • @VaughnCato Well yes, that's my goal. I want to wrap that little std::get call in something nicer. Why, because I want a clean interface in my project. I still hope, that C++11 can be pretty, too ;-) So for me nice possibilities would be "get(myEnum::type1, tuple)" or "tuple.get(myEnum::type1)". – Mike M Jul 19 '13 at 14:27
  • Can you associate particular enums with particular tuple types? If so, you might just want to overload your `get<>` template for the various tuple types. – Vaughn Cato Jul 19 '13 at 18:29
  • @VaughnCato: Yes, the tuples know, to which enum they are associated. Not the other way round, since there may be several tuples for each enum. – Mike M Jul 19 '13 at 19:52

1 Answers1

1

I think the best you are going to be able to do is to create a get<>() function for each enumeration. Here is an example:

#include <tuple>
#include <string>
#include <iostream>

typedef std::tuple<std::string,std::string> Tuple1;
typedef std::tuple<std::string,int> Tuple2;

enum class Enum1 {
  name,
  address
};

enum class Enum2 {
  state,
  zip_code
};

template <typename Enum>
constexpr std::size_t indexOf(Enum value)
{
  return static_cast<std::size_t>(value);
}

template <typename Enum,Enum enum_value,typename Tuple>
constexpr auto get(const Tuple &value)
  -> decltype(std::get<indexOf(enum_value)>(value))
{
  return std::get<indexOf(enum_value)>(value);
}

template <Enum1 enum_value>
constexpr auto get(const Tuple1 &value)
  -> decltype(get<Enum1,enum_value>(value))
{
  return get<Enum1,enum_value>(value);
}

template <Enum2 enum_value>
constexpr auto get(const Tuple2 &value)
  -> decltype(get<Enum2,enum_value>(value))
{
  return get<Enum2,enum_value>(value);
}

int main(int,char**)
{
  Tuple1 a("John","123 Foo St");
  Tuple2 b("California",90210);
  std::cerr << get<Enum1::name>(a) << "\n";
  std::cerr << get<Enum1::address>(a) << "\n";
  std::cerr << get<Enum2::state>(b) << "\n";
  std::cerr << get<Enum2::zip_code>(b) << "\n";
}

It is tedious, however this does have the benefit of compile-time checking that the enumerations are compatible with the tuples.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • Thank you for your efforts. I was hoping to get around the explicit instantiation for Enum1 and Enum2. What I still don't get is, why template constexpr auto get(Enum enum_value, const Tuple &value) -> decltype(std::get(value)) is not possible (use of parameter 'enum_value' outside function body) but template auto add(T t, U u) -> decltype(t + u); is ok. – Mike M Jul 22 '13 at 09:10
  • @MikeM: In the case of `template auto add(T t,U u) -> decltype(t+u)`, the return type of the function only depends on the types of the arguments, which are known at compile time. In the case of `template constexpr auto get(Enum enum_value,const Tuple &value) -> decltype(std::get(value))`, the return type depends on the value of the argument, which is not known at compile time (in general). – Vaughn Cato Jul 22 '13 at 13:34
  • Thank you very much. Now I see the difference. So, one would need something like constexpr for parameters? I'm tring to wrap my head about the underlying rules. I just found out, that while your indexOf works fine, this doesn't work `template struct Test{Test(Enum value) {...}}` and calling it `Test(myEnum::type1);` Well, still learning. Thank you for your efforts. – Mike M Jul 22 '13 at 13:47