Unknown return type from variadic template list

Menu<T, Rest...> has type T for its options, while its submenus will have type U, where U is the first type in the pack Rest... The specialization Menu<T> has items of type T and no submenus.

How to declare the return type for Menu<T, Rest...>::choose()? The following below compiles except for the all-important choose() line in main().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <string>

template <typename...> struct Menu;

template <typename T>
struct Menu<T> {
	struct Option {
		T item;
	};
	Option* option;  // Assume only one option for simplicity here.
	T choose() const {return option->item;}
	void insert (const T& t) {option = new Option{t};}
};

// A specialization for the Menu instances that will have submenus.
template <typename T, typename... Rest>
struct Menu<T, Rest...> {  // Menu with T as its options type.
    struct Option {
        const T item;
        Menu<Rest...>* submenu;  // Submenu with the first type in Rest... as its options type.
    };
    Option* option;
 	T choose() const {  // The problem function.
        // In reality there will be user input, of course.  The user might not choose to enter a submenu,
        // but instead choose from among the options in the current menu.
	 	if (option->submenu)  
		 	return option->submenu->choose();
		return option->item;
	}
	void insert (const T& t, Menu<Rest...>* submenu = nullptr) {option = new Option{t, submenu};}
}; 
        
int main() {
    Menu<int, std::string, char> menu;
    Menu<std::string, char> submenu;
    Menu<char> subsubmenu;
    subsubmenu.insert('a');
    submenu.insert ("", &subsubmenu);
    menu.insert (0, &submenu);
//    const auto choice = menu.choose();  // Won't compile.
}


I tried returning void* but with no luck.
Last edited on
Have you tried marking the return type auto and then compiling in C++14?
1
2
3
return option->submenu->choose();
return option->item;
Menu<int, std::string, char> menu;
So, it returns either int, string or char depending on existence of submenus? This is not going to work. Function can have only one return type.

You can use something like boost::variant to allow pseudo-different return types.
I'll use boost if there is no other choice. But I still haven't been able to study boost yet. How does the syntax work?

1
2
3
4
5
        typename boost::variant<T, Rest...> choose() const {
	 	if (option->submenu)  
		 	return option->submenu->choose();
		return option->item;
	}

Like that?
Sticking with C++11 for the moment, I've come up with a semi-satisfactory solution. All Menu<...> instances will return ChooseVisitor, whose stored item is the desired chosen item. But I'm still seeking a better solution, because it seems ugly (and slow).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
#include <string>

struct Visitor {
    virtual void visit (int&) = 0;
    virtual void visit (std::string&) = 0;
    virtual void visit (char& c) = 0;
};

struct ChooseVisitor : Visitor {
    std::pair<int, bool> chosenInt;
    std::pair<std::string, bool> chosenString;
    std::pair<char, bool> chosenCharacter;
    virtual void visit (int& num) override {
    	chosenInt.first = num;
	chosenInt.second = true;
   }
    virtual void visit (std::string& str) override {
	chosenString.first = str;
	chosenString.second = true;
   }
    virtual void visit (char& c) override {
    	chosenCharacter.first = c;
    	chosenCharacter.second = true;
   }
};

template <typename...> struct Menu;

template <typename T>
struct Menu<T> {
	struct Option {
		T item;
		void accept (ChooseVisitor& visitor) {visitor.visit(item);}
	};
	Option* option;  // Assume only one option for simplicity here.
	ChooseVisitor choose() const {
		ChooseVisitor visitor;
		option->accept(visitor);
		return visitor;
	}
	void insert (const T& t) {option = new Option{t};}
};

// A specialization for the Menu instances that will have submenus.
template <typename T, typename... Rest>
struct Menu<T, Rest...> {  // Menu with T as its options type.
    struct Option {
        T item;
        Menu<Rest...>* submenu;  // Submenu with the first type in Rest... as its options type.
        void accept (ChooseVisitor& visitor) {visitor.visit(item);}
    };
    Option* option;
    ChooseVisitor choose() const {
        // In reality there will be user input, of course.  The user might not choose to enter a submenu,
        // but instead choose from among the options in the current menu.
        ChooseVisitor visitor;
	if (option->submenu)  
	    return option->submenu->choose();
	else
	    option->accept(visitor);
	return visitor;
    }
    void insert (const T& t, Menu<Rest...>* submenu = nullptr) {option = new Option{t, submenu};}
}; 
        
int main() {
    Menu<int, std::string, char> menu;
    Menu<std::string, char> submenu;
    Menu<char> subsubmenu;
    subsubmenu.insert('t');
    submenu.insert ("", &subsubmenu);
    menu.insert (0, &submenu);
    const ChooseVisitor visitor = menu.choose();
    if (visitor.chosenInt.second)
	std::cout << "You chose " << visitor.chosenInt.first << ".\n";  // Do whatever with it.
    else if (visitor.chosenString.second)
	std::cout << "You chose " << visitor.chosenString.first << ".\n";  // Do whatever with it.
    else if (visitor.chosenCharacter.second)
	std::cout << "You chose " << visitor.chosenCharacter.first << ".\n";  // Do whatever with it.
}

Output:
 
You chose t.

The biggest problem is that ChooseVisitor needs to be constantly updated for all possible Menu option types (it could end up with literally hundreds of data members and overloads), not to mention the horrendous if-checks to get the desired returned item.

I welcome better suggestions.
Last edited on
Something like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <utility>
#include <typeindex>

template < typename T > struct result : std::pair< T, std::type_index >
{ result( T v ) : std::pair< T, std::type_index >( v, typeid(v) ) {} };

template < typename... > struct type_selector ;

template < typename T > struct type_selector<T>
{ static  result<T> select( T v ) { return v ; } };

template < typename FIRST, typename... REST > struct type_selector<FIRST, REST...>
{
    static  result<FIRST> select( FIRST v ) { return type_selector<FIRST>::select(v) ; }

    template < typename T > static  result<T> select( T v ) { return type_selector<REST...>::select(v) ; }
};

http://coliru.stacked-crooked.com/a/accb16e34a43dc23
Topic archived. No new replies allowed.