2

I am trying to generalize a function for a game engine I am writing to simplify the shader loading process.

Anyway, the difficulty arises in my attempts to templating the function. I attempted to adapt the solution found in this stack thread, and resulted in various versions of the following code without any ideal solutions:

template<typename T, typename U, U T::* x>
U testFunc(T& t)
{ 
    // Simplification of the extremely generalized function;
    // I want to return/utilize member variable x of input t
    return x;
}

struct testS1 { int a = 100; };
struct testS2 { bool b = true; };

testS1 tS1;
testS2 tS2;

// would ideally return 100
int c = testFunc<testS1, int, &testS1::a>(tS1);

// would ideally return true 
bool d = testFunc<testS2, bool, &testS2::b>(tS2);

Running the program gives the following error:

Severity: Error
Line: 46
Code: C2440
Description: 'return': cannot convert from 'int testS1::* ' to 'U'

I understand that the returned value x is not the same type as U because int testS1::* is not the same type as int.

However, I do not understand what syntax I would use to return the member variable x of type U from the struct T.

The real structs behind the placeholders testS1 and testS2 are very different from each other, so I would like to avoid using a base class/struct if at all possible.

JeJo
  • 30,635
  • 6
  • 49
  • 88

2 Answers2

4
template<class T, class U, U T::*x>
U testFunc(T& t)
{
    return t.*x;
}

You simply need to dereference x to get a U.

YSC
  • 38,212
  • 9
  • 96
  • 149
4

The other answer has already provided the syntax for accessing the pointer to the member data. In using abbreviated function template, you could do it, less verbosely the same

template<auto  x>
constexpr auto testFunc(auto const& t)
{
    return t.*x;
}

Now you call with less explicate template arguments

int c = testFunc<&testS1::a>(tS1);
bool d = testFunc<&testS2::b>(tS2);
// ...

See a demo in godbolt.org

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Thank you for clarifying what Pepijn answered with. What if I wanted to return an std::vector? Such that the call-line is `std::vector testVec = testFunc<&testS1::a>(tS1);` and the testFunc becomes something like `template constexpr auto testFunc(auto const& srcVec) { std::vectordstVec(srcVec.size()); std::transfer(srcVec.begin(), srcVec.end(), dstVec.begin(), [](auto const& mSrc){ return mSrc.*x; }); return dstVec; }` would it be possible to simplify it down to the single input template function, or no because of the internal std::vector? – ModernEraCaveman Jul 02 '23 at 20:04
  • 1
    @ModernEraCaveman Yes, of course with a help of helper trait: https://gcc.godbolt.org/z/YGb83o3E3 – JeJo Jul 02 '23 at 20:43
  • You're a wizard, JeJo! Helper traits look like complete magic because idk how they work, but it works lol. Thank you so much for your help. – ModernEraCaveman Jul 02 '23 at 20:56
  • 2
    Take my upvote. In my hurry I missed this better solution. @ModernEraCaveman I won't mind this answer to get the checkmark ;) – YSC Jul 03 '23 at 18:42
  • On it I was struggling to decide on giving them the checkmark since `auto` may not work in every use case, but you've made that decision easy to make. Nonetheless, I appreciate your speed in providing a workable solution! – ModernEraCaveman Jul 03 '23 at 18:57