2

I'm trying to find the best method to have a kind of "object" that can be either specialized or "linked" to another type.

For instance you cannot specialize a class to make it become a simple int, and you cannot use the keyword using to specialize classes.

My solution is the following:

template<class Category, Category code>
struct AImpl
  {};

template<class Category, Category code>
struct AHelper
  {
  using type = AImpl<Category, code>;
  };

template<class Category, Category code>
using A = typename AHelper<Category, code>::type;

template<int code>
void doSomething(A<int, code> object)
  {
  }

template<>
struct AImpl<int, 5>
  { 
  double a; 
  };

template<>
struct AImpl<int, 6>
  { 
  int b; 
  double c;
  };

template<>
struct AHelper<int, 7>
  {
  using type = int;
  };

template<class Category, Category code>
struct Alternative {};

template<int code>
void doSomethingAlternative(Alternative<int, code> object)
  {
  }

This works but you need to specify the code parameter in doSomething, and I would like to avoid that.

For instance:

A<int,7> a7; // This is equivalent to int
a7 = 4;
A<int, 5> a5; // This is equivalent to AImpl<int,5>
a5.a = 33.22;
doSomething(a5); // This does not compile
doSomething<5>(a5); // This compiles but is bulky
Alternative<int,0> alt0;
doSomethingAlternative(alt0); // This compiles and is not bulky
                              // but you're forced to use class
                              // specializations only

Is there a way to achieve what I want? It's ok to change both doSomething or the A implementation.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
Saturnu
  • 309
  • 1
  • 8
  • There are no template specializations in your code. It is not clear to me what the overall goal here is beyond the exact implementation you show. I assume you want to be able to swap out `AImpl` later on, but it's not clear why you don't do that directly via `using A = AImpl1<...>`. – Max Langhof May 03 '19 at 12:10
  • In other words, what is the point of `AHelper`? It doesn't seem necessary in the code you show, so could you extend the example to include a purpose for the `AHelper` part (which seems to be the core of the problem)? – Max Langhof May 03 '19 at 12:13
  • @MaxLanghof you're right, code changed. I hope it's more clear now – Saturnu May 03 '19 at 12:16
  • I've added some other code to show you the alternative. – Saturnu May 03 '19 at 12:22
  • You omitted the implementation of `doSomething` for obvious reasons, but what code is it that works exactly the same for both fundamental types and class types? And how is `Category` needed in such a function for a plain integer? – StoryTeller - Unslander Monica May 03 '19 at 12:23
  • @StoryTeller there are many possible usages, but the point is that this approach can be used not only with fundamental types but you can also link to other classes. For instance you can make it link to int, std::vector, whatever you want – Saturnu May 03 '19 at 12:26

1 Answers1

2

If you are trying to customize the behavior of doSomething based on the type it is called with, you cannot have the compiler deducing stuff from AHelpr::type (as previously answered). But you can tell it what it needs to know by providing a customization point in the form of traits. For instance:

template<typename T>
void doSomething(T& object)
{
    auto code = SmthTriats<T>::code;
}

This is highly extendable given the ability to specialize SmthTriats:

template<typename> struct SmthTriats;

template<typename Category, Category code_>
struct SmthTriats<AImpl<Category, code_>> {
    static constexpr auto code = code_;
};

template<>
struct SmthTriats<int> {
    static constexpr auto code = 7;
};

A traits class allows for customization outside your module too. Client code need only specialize SmthTriats with their own type, and so long as they respect the contract you laid out with your trait, your code will work for them.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Interesting, would this work in C++11 too? I think however it's something different that what I'm looking for though – Saturnu May 03 '19 at 12:40
  • @Saturnu - Of course. The idea of traits is almost as old as C++ templates. The standard library makes heavy use of them itself. They are a convenient way to provide interesting properties about a type (or family of types, in the case of partial specializations). – StoryTeller - Unslander Monica May 03 '19 at 12:42
  • If you are looking to make your original attempt work, I urge you not to get fixated on a solution. Give this a try in your code base. This technique is industry standard for a good reason. – StoryTeller - Unslander Monica May 03 '19 at 12:43
  • I totally agree with you about not getting fixated on a solution. Yep, I know traits, you can consider AHelper a kind of traits too. I'm going to check well your solution and give you a feedback asap. – Saturnu May 03 '19 at 12:48
  • 1
    @Saturnu - Here's another way to think about it. `AHelper` is indeed a trait (a map to a type). The compiler can't assume the mapping is two way (as mentioned). You need to tell it how to map in reverse via another trait. – StoryTeller - Unslander Monica May 03 '19 at 12:50
  • Yep interesting explanation. The fact is that the solution you've posted doesn't seem to fit well for my particular case. – Saturnu May 03 '19 at 12:58
  • Ps were you wrote T, did you mean Category right? The fact is that I'm not trying to get types / values from a type, I need the type itself – Saturnu May 03 '19 at 13:05
  • @Saturnu - In `doSomething`? No, I meant `T`. The type of the argument is open ended (otherwise, how would `int` be supported?). The trait is what analyzes T and tells us properties about it. – StoryTeller - Unslander Monica May 03 '19 at 13:07
  • I think that then you need to add a class Category over there, just to make it compile – Saturnu May 03 '19 at 13:09
  • 1
    @Saturnu - I see what you meant. Yes, fixed the trait. – StoryTeller - Unslander Monica May 03 '19 at 13:11
  • Ok, now I've understood your solution. It doesn't apply for what I'm doing but it's quite cool, I think I can use it in some way to improve the solution I posted. The fact is that I don't need to get values / types from a type , I need to operate on the type itself. Serialization in my specific case. Probably I just need to pass another traits like the one you wrote in one of the above functions – Saturnu May 03 '19 at 13:24
  • I've tried to use your approach, creating a kind of reverse traits helper. It works only in certain cases. It is not possible to map same type to different Category / code combinations. It's an interesting idea though – Saturnu May 03 '19 at 13:47
  • best and only answer so far, so you totally deserve the accepted answer :) Only problem is as I said that you cannot use a type more than once (e.g. int for two different codes) – Saturnu May 06 '19 at 09:20