Store member get/set functions for later use

Viewed 82

I want to store a pair of member get/set functions for later use with an object of type T.

I have a partially working setup, see below. Questions remain however:

  • How do I (smartly) deal with all possible variants? member_get could be [returning value or const value& or even value& | const or non-const] member_set could be [accepting const &, & or &&]. Sure, 'best practices' would rule out some combinations, but I cannot rely on that as the definition of member_get and member_set is out of my hands.

  • How do I correctly deal with possible member_set move semantics?

  • Is there a different/better/simpler general way to approach this?

Notes:

  • I intentionally left open the exact type S of the setter. Not sure if that's a good or bad idea.
  • Lambdas obviously come to mind, but I can't see how they can help the issue. The caller of Make( get, set ) is not supposed to supply lambdas. That would be just delegating the problem to him!?
  • any std::function ideas should be ruled out because of the overhead
template <typename T, typename V, typename G, typename S>
class GetSet
{
public:

    constexpr GetSet( G member_get, S member_set ) : Get( member_get ), Set( member_set )
    { }

    auto GetValue( const T& t ) const 
    {
        return ( t.*Get )( );
    }

    void SetValue( T& t, V&& value ) const
    {
        ( t.*Set )( std::forward<V>( value ) );
    }

private:
    G               Get;
    S               Set;
};

template <typename T, typename ValueType, typename S>
constexpr auto Make( ValueType( T::*member_get )( ) const, S member_set )
{
    using G = ValueType( T::* )( ) const;
    return GetSet<T, ValueType, G, S>( member_get, member_set );
}
1 Answers

Not sure why you need this, but the simplest solution is below.

#include <utility>

template <class F> struct ClassType;

template <typename Ret, typename TCls, typename... Args>
struct ClassType<Ret (TCls::*)(Args...)> {
    using type = TCls;
};

template <typename Ret, typename TCls>
struct ClassType<Ret (TCls::*)() const> {
    using type = TCls;
};


template<class TFnGet, class TFnSet>
class GetSet
{
   public:
      using TGet = TFnGet;
      using TSet = TFnSet;
   public:
      inline GetSet(TGet fnGet, TSet fnSet)
         : m_fnGet(fnGet), m_fnSet(fnSet)
      {
      }

      template<class T>
      inline decltype(auto) GetValue(T&& r) const
      {
         static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnGet>::type>::value, "wrong type of r?");
         return (r.*m_fnGet)();
      }

      template<class T, class TValue>
      inline void SetValue(T&& r, TValue&& value)
      {
         static_assert(std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, typename ClassType<TFnSet>::type>::value, "wrong type of r?");
         (r.*m_fnSet)(std::forward<TValue>(value));
      }

   private:
      TGet m_fnGet;
      TSet m_fnSet;
};


template<class TGet, class TSet>
GetSet<TGet, TSet> MakeGetSet(TGet fnGet, TSet fnSet)
{
   static_assert(std::is_same<typename ClassType<TGet>::type, typename ClassType<TSet>::type>::value, "members of different classes?");
   return GetSet<TGet, TSet>(fnGet, fnSet); 
}

verified with:

class A
{
   public:

   void Set(int i) {}
   int Get() const { return 0;}
   
   void SetRef(char& ch) {}
   A& GetRef() { return *this;}
   
   void SetRRef(float&& ) {}
   int&& GetRRef() { return 1; }

   void SetConstRef(const char& ch) {}
   int GetNotConst() { return 0;}
};


int main(int argc, char* argv[])
{
   A a;

   auto gs = MakeGetSet(&A::Get, &A::Set);
   auto gsRef = MakeGetSet(&A::GetRef, &A::SetRef);
   auto gs2 = MakeGetSet(&A::GetRRef, &A::SetRRef);
   auto gsNonConst = MakeGetSet(&A::GetNotConst, &A::SetConstRef);

   int x = gs.GetValue(a);
   gs.SetValue(a, 2);
   const A& ra = a;
   x = gs.GetValue(ra);

   A& r = gsRef.GetValue(a);
   char ch =' ';
   gsRef.SetValue(a, ch);
   
   x = gs2.GetValue(a);
   gs2.SetValue(a, 1.1f);
   
   x = gsNonConst.GetValue(a);
   gsNonConst.SetValue(a, ch);

   std::cout << "ok\n";
   return 0;
}
Related