2

I want to create a c++ class, in which I can add members of types (int, long, double, string). I am very new to template programming in c++.

I have been following this guide : https://eli.thegreenplace.net/2014/variadic-templates-in-c/ for the variadic data structures. But how can I input a type (int, long, etc.) and its value and add that member to my class?

For example, if this is how I define the tuple class:

template <class... Ts> struct tuple {};

template <class T, class... Ts>
struct tuple<T, Ts...> : tuple<Ts...> {
  tuple(T t, Ts... ts) : tuple<Ts...>(ts...), tail(t) {}

  T tail;
};

Then I would want to take input at runtime and:

while(cin>>type) {
  cin>>value;
  // add type and value to my class
}

And the input could be int 3 double 3.5 string hello. My class should be in that case tuple<int, double, std::string> t(3, 3.5, "hello").

Is there a way I could achieve this using template programming?

user17836
  • 103
  • 1
  • 7
  • 4
  • @StoryTeller if I restrict the type to `std::string`, then can I keep adding members to the class? – user17836 Nov 27 '17 at 18:47
  • 3
    That would still be a no. The types of the tuple must be known *statically*. There are techniques to deal with unknown types at run-time. But you can't arbitrarily add members to a class type. – StoryTeller - Unslander Monica Nov 27 '17 at 18:48
  • Related: [C++ Tuple vs Struct](https://stackoverflow.com/questions/5852261/c-tuple-vs-struct) – user0042 Nov 27 '17 at 18:51
  • Member names are gone by the time the compiler is done, replaced by an offset in memory from the beginning of the object (or something else that can be made to work should the compiler writer be insane or experimenting with something interesting). Consider using a `std::map` to store a pairing of runtime name and runtime value. – user4581301 Nov 27 '17 at 18:52
  • 1
    You can add members to a class at run time, e.g. if your language is called Python. In C++ you use `std::vector>`. – n. m. could be an AI Nov 29 '17 at 18:07
  • ... or, if you're into the occult, [`std::any`](https://en.cppreference.com/w/cpp/utility/any). – bitmask Apr 26 '23 at 13:29

1 Answers1

2

If you really want to work with statically typed, say, std::tuple<int, int, char, int> based on information from cin then your compiler has to "prepare" all the possible paths at compile time. Depending on the number of types N and the maximum length s there are different possibilites, i.e., the number of possibilites grows exponentially with the maximum length s. For very small N and s this could work. Since you probably prefer another approach (without statically typed, say, std::tuple<int, int, char, int>) I prepared a C++17 example treating the types only.

#include <cstdint>
#include <iostream>
#include <tuple>

template<
  class F,
  class Tuple=std::tuple<>
>
auto tuplify_cin_and_call(
  F f,
  Tuple tuple=Tuple{}
) {
  constexpr std::size_t max_tuple_size = 6;

  std::cout << __PRETTY_FUNCTION__ << std::endl;

  if constexpr(1 + std::tuple_size<Tuple>::value < max_tuple_size) {
    std::cout << "`int`|`char` to append or `done` to finish: " << std::flush;
    std::string input{};
    std::cin >> input;

    if(input == std::string{"int"}) {
      tuplify_cin_and_call(f, std::tuple_cat(tuple, std::tuple<int>{}));
    }
    else if(input == std::string{"char"}) {
      tuplify_cin_and_call(f, std::tuple_cat(tuple, std::tuple<char>{}));
    }
    else if(input == std::string{"done"}) {
      return f(std::move(tuple));
    }
    else {
      std::cout << "ERROR: invalid input" << std::endl;// `cout` or `cerr` here?
      return tuplify_cin_and_call(f, std::move(tuple));
    }
  }
  else {
    std::cout << "max size reached. `done` to finish: " << std::flush;

    std::string input{};
    std::cin >> input;

    if(input == std::string{"done"}) {
      return f(std::move(tuple));
    }
    else {
      std::cout << "ERROR: invalid input" << std::endl;// `cout` or `cerr` here?
      return tuplify_cin_and_call(f, std::move(tuple));
    }
  }
}

int main() {
  tuplify_cin_and_call(
    [] (auto tuple) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
  );
  return 0;
}

Output (with my keyboard input included):

./main
auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<>]
`int`|`char` to append or `done` to finish: int 
auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<int>]
`int`|`char` to append or `done` to finish: char
auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<int, char>]
`int`|`char` to append or `done` to finish: asdf
ERROR: invalid input
auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<int, char>]
`int`|`char` to append or `done` to finish: char char int
auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<int, char, char>]
`int`|`char` to append or `done` to finish: auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<int, char, char, char>]
`int`|`char` to append or `done` to finish: auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<int, char, char, char, int>]
max size reached. `done` to finish: asdf
ERROR: invalid input
auto tuplify_cin_and_call(F, Tuple) [with F = main()::<lambda(auto:1)>; Tuple = std::tuple<int, char, char, char, int>]
max size reached. `done` to finish: done
main()::<lambda(auto:1)> [with auto:1 = std::tuple<int, char, char, char, int>]
Julius
  • 1,816
  • 10
  • 14