2

Here is my example class for accessing memory:

class memory {
  public:
    memory();

    template <typename T>
    T get(int index);
    template <typename T>
    void set(int index, T value);
};

And here I use it:

int main(){
  memory mem;

  float f = mem.get<float>(0);
  char* str = mem.get<char*>(1);

  mem.set<float>(0, 25);  // int passed, upgraded to float

  return 0;
}

And I'd like to use it this way:

int main(){
  memory<float, char*> mem;  // typenames defined only once. Must accept a variable number of template arguments

  float f = mem.get(0);
  char* str = mem.get(1);

  mem.set(0, 25);  // int passed, upgraded to float

  return 0;
}

How can I implement this? Is it even possible?

user3600124
  • 829
  • 1
  • 7
  • 19
  • You could use `typeid` maybe. – user0042 Aug 06 '17 at 14:12
  • 9
    You seem to be trying to reinvent [std::tuple](http://en.cppreference.com/w/cpp/utility/tuple) – Igor Tandetnik Aug 06 '17 at 14:14
  • Why are you doing this? What is the problem you are attempting to solve with this class? Are you sure you won't be breaking [the strict aliasing rule](http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule)? – Some programmer dude Aug 06 '17 at 14:15
  • By the way, with your original class as you show, you don't have to provide the type to `set`, the compiler will be able to deduce it. So you can do just `mem.set(0, 2.5f)` – Some programmer dude Aug 06 '17 at 14:17
  • I have to make sure the right type is passed for a compiler to deduce it properly. And I am not entirely happy about that – user3600124 Aug 06 '17 at 14:25

1 Answers1

1

You seem to want std::tuple or hana::tuple. It's entirely possible to change you class so that you don't need to send the type each time.

However, you will still need to pass something as a template parameter. Usually, the index of the variable or the index of the type is enough.

The tuple class from the standard library does it like this:

std::tuple<int, std::string> tup;

std::get<0>(tup) = 5; // assign the int
std::get<1>(tup) = "test"; // assign the string

Boost hana skies it in a similar way, but uses operator[]:

hana::tuple<int, std::string> tup;

tup[0_c] = 5; // assign the int
tup[1_c] = "test"; // assign the string

The _c is a user provided literal that transform the int into an integral constant, so the value can be used at compile time.


So how you'd do it?

Simply change your int parameter to a template parameter:

int main() {
  memory<float, char*> mem;

  float f = mem.get<0>();
  char* str = mem.get<1>();

  mem.set<0>(25);  // int passed, upgraded to float

  return 0;
}

And then, to infer what the type is according to the index, use something like this:

template<std::size_t, typename>
struct memory_element; // no basic case

// In this case, we pass a number and a memory class
// We extend the case were the index is decremented, and we remove the first type.
template<std::size_t index, typename Head, typename... Tail>
struct memory_element<index, memory<Head, Tail...> : memory_element<index - 1, memory<Tail...>> {};

// Case where we get to 0
template<typename T, typename... Rest>
struct memory_element<0, memory<T, Rest...>> {
    using type = T;
};

// Alias for ease of use
template<std::size_t I, typename M>
using memory_element_t = typename memory_element<I, M>::type;

You can the use it like this:

int main () {
    // T1 is int
    using T1 = memory_element_t<0, memory<int, float>>;

    // T2 is float
    using T2 = memory_element_t<1, memory<int, float>>;
}
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141