5

Note: the example provided in this question is not production code and has no sense at all. It is just there to illustrate my problem.

I was testing the possibilities of decltype, especially if it is used to deduce function parameter types, and ran into a problem:

Suppose there were two classes structured like this:

struct ClassInt
{
    // Note: no default ctor
    ClassInt(int value)
        :   m_Value(value)
    {}

    int  m_Value;
};

struct ClassDouble
{
    // Note: no default ctor
    ClassDouble(double value)
        :   m_Value(value)
    {}

    double  m_Value;
};

Now, I wrote a function that (somehow) retrieves an instance of a the type parameter (which should be one of the above) by string and assigns a given value to its m_Value member:

template< typename Ty >
Ty* get_fake_ptr() { return nullptr; }


// Retrieve pointer to Ty object by name and assign its value member.
// The problem is that we don't actually have an instance of Ty at the point
// where we want to define the type of the parameter "value".
template< typename Ty >
void assign(std::string name, decltype(get_fake_ptr<Ty>()->m_Value) value)
{
    // Somehow get pointer to a Ty object by name
    Ty* obj = ????;

    // Assign
    obj->m_Value = value;
}

Now, the type of the parameter value is dependent on the type parameter, as the used classes differ in the type of the member m_Value. As you can see, I solved it by using decltype. Now, normally, you would use decltype on a parameter, like this:

template<typename Ty>
void assign(Ty& obj, decltype(obj.m_Value) value);

But that is obviously not possible here since the actual instance is retrieved in the function body and is thus not available at the point where function arguments are declared.

I hacked it together by using the template function get_fake_ptr which just returns a nullptr of matching type so I have a "pseudo instance" that the compiler can use to determine the member type. And it works:

working

Now, as I said, this seems really hacky to me. So:

Is there a better way to solve this problem?

Thank you!

nshct
  • 1,197
  • 9
  • 29
  • 2
    You can use `decltype(std::declval().m_Value)` to deduce the type of T's m_Value member instead of using your `get_fake_ptr()` function. They accomplish similar goals in the end, I suppose. – bku_drytt Sep 21 '15 at 03:10
  • 1
    By the way, your `get_fake_ptr()` function does not need to be defined. You can simple leave it as `template T* get_fake_ptr();` and you can still use it inside `decltype`. – bku_drytt Sep 21 '15 at 03:20
  • 1
    What's wrong with `decltype(Ty::m_value)`? – Casey Sep 21 '15 at 03:26
  • @bku_drytt `std::declval<>` seems to be the way to go. Could you write a quick answer so I can accept it? – nshct Sep 21 '15 at 03:30

3 Answers3

3

I see nothing wrong with this approach.

When it comes to template meta-programming, these kinds of "hacks" are par for the course.

I can suggest, though, a slightly different design pattern. Something like this:

struct ClassInt
{
    // Note: no default ctor
    ClassInt(int value)
        :   m_Value(value)
    {}

    typedef int m_value_t;

    m_value_t  m_Value;
};

struct ClassDouble
{
    // Note: no default ctor
    ClassDouble(double value)
        :   m_Value(value)
    {}

    typedef double m_value_t;

    m_value_t  m_Value;
};

// ...
template< typename Ty >
void assign(std::string name, typename Ty::value_t value)
{
    // Somehow get pointer to a Ty object by name
    Ty* obj = ????;

    // Assign
    obj->m_Value = value;
}
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
3

You can use decltype(std::declval<T>().m_Value) to deduce the type of T's m_Value member instead of using your get_fake_ptr() function. They accomplish similar goals in the end.

By the way, your get_fake_ptr() function does not need to be defined. You can simple leave it as template <typename T> T* get_fake_ptr(); and you can still use it inside decltype.

bku_drytt
  • 3,169
  • 17
  • 19
2

Another alternative is to add another template parameter and just let the assignment handle matching types and conversions. Any non-convertable value types will generate a compile error.

template<typename Ty, typename VALUE_TYPE>
void assign(std::string name, VALUE_TYPE value) {
  Ty* obj = ???
  obj->m_Value = value;
}
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187