Home > Software design >  overlaoding the function template whose parameters are same
overlaoding the function template whose parameters are same

Time:11-25

It's a pity that there are some APIs provided by others that are out of my control and the said APIs return the same type(i.e. uint8_t other than the enum type).

But the meaning is different when the same value is returned by the different APIs. I hope to use overloading to convert the returned value to readable strings.

However, as per this answer, which says:

a typedef is just another name for the same type. But you can only overload on different types.

I thought and thought, and I found a solution. But I think there must be a better one. How?

But when I finish the code snippet below. I am more confused. Since Demo::Foo::SampleType and Demo::Bar::SampleTypeare the same thing, so the overloading should not work. But the code snippet works well! What a surprise!

Here is the demo code snippet for my solution:

#include <iostream>
#include <map>


namespace Demo
{
    class Foo
    {
        public:
         using SampleType = uint8_t;
         SampleType GetFooData(){return SampleType{1};}
    };

    class Bar
    {
        public:
         using SampleType = uint8_t;
         SampleType GetBarData(){return SampleType{2};}
    };
}

//////////////////code above is provided by others/////////////
template <typename T>
void FindAndPrint(const T& val, const std::map<T, std::string>& mp)
{
    auto itr = mp.find(static_cast<T>(val));
    if(itr != mp.end())
    {
        std::cout << itr->second;
    }
    else
    {
        std::cout << "can't classify";
    }

    std::cout << std::endl;
}


template <typename ServiceName>
void Print(const typename ServiceName::SampleType& data);

template <>
void Print<Demo::Foo>(const typename Demo::Foo::SampleType& data)
{
    enum class Color
    {
        Blue = 1,
        Red = 2,
    };
    
    std::map<Color, std::string> mp ={
        {Color::Blue, "Blue"},
        {Color::Red, "Red"},
    };
    
    FindAndPrint(static_cast<Color>(data), mp);
    
    return;
}

template <>
void Print<Demo::Bar>(const typename Demo::Foo::SampleType& data)
{
    // similar code like above
    // When I write this line, I start to realize my solution is wrong since `Demo::Foo::SampleType` and `Demo::Foo::SampleType`are the same thing! 
    // But, what a surprise! This code snippet compiles and works well. Why?

    enum class Animal
    {
        Dog = 1,
        Cat = 2,
    };
    
    std::map<Animal, std::string> mp ={
        {Animal::Dog, "Dog"},
        {Animal::Cat, "Cat"},
    };
    
    FindAndPrint(static_cast<Animal>(data), mp);

    return;
}

int main()
{
    Demo::Foo foo;
    Print<Demo::Foo>(foo.GetFooData());

    Demo::Bar bar;
    Print<Demo::Bar>(bar.GetBarData());
}

CodePudding user response:

What you have is not function overload. It is template specialization. The 3 functions you have:

template <typename ServiceName>
void Print(const typename ServiceName::SampleType& data);

template <>
void Print<Demo::Foo>(const typename Demo::Foo::SampleType& data);

template <>
void Print<Demo::Bar>(const typename Demo::Foo::SampleType& data);

It doesn't really matter how you spell out the parameter type, as long the deduced type is consistent with how you primary template was defined. Which mean you can also write them as:

template<>
void Print<Demo::Foo>(const uint8_t& data);

And the reason Print<Demo::Foo>(...) and Print<Demo::Bar>(...) are considered different functions is because you are providing different template parameters. As long Demo::Foo and Demo::Bar are not the same type, then they are considered 2 different template specialization of your base template.

CodePudding user response:

You can put all the specifics about how to decode the uint8_t in a template class SampleTypeInfo that you specialize for the services. If you do that properly, the Print function can be collapsed to

template <typename ServiceName>
void Print(const typename ServiceName::SampleType& data) {
  typedef SampleTypeInfo<ServiceName> Info;
  FindAndPrint(Info::decode(data), Info().stringMap);
}

Here is how you can implement SampleTypeInfo:

template <typename T> struct SampleTypeInfo {};

template <typename T>
struct SampleTypeInfoBase {
  typedef T SampleType;
  
  static T decode(uint8_t x) {
    return static_cast<T>(x);
  }

  std::map<T, std::string> stringMap;
protected:
  void regString(T k, const std::string& v) {
    stringMap[k] = v;
  }
};

template <>
struct SampleTypeInfo<Demo::Foo> : public SampleTypeInfoBase<Color> {
  SampleTypeInfo() {
    regString(Color::Blue, "blue");
    regString(Color::Red, "red");
  }
};

template <>
struct SampleTypeInfo<Demo::Bar> : public SampleTypeInfoBase<Animal> {
  SampleTypeInfo() {
    regString(Animal::Cat, "cat");
    regString(Animal::Dog, "dog");
  }
};
  • Related