2

I have a class which is derived off a class provided in a third party library.

Between versions of the third party library, they promoted a member in one of their classes from private to public, but in the move, they deprecated a method for accessing the member. Example:

// Old class
class A
{
    public:
        int &GetA() { return a;}
    private:
        int a;
};

// NewClass
class A
{
    public:
        int a;
};

My code uses an instance of A, but I want my code to work with either version, in case someone hasn't updated the library.

class B
{
   public:

   int & ReturnA() { return GetInnards(m_a);}

   private:
   A    m_a;

  // If m_a has a GetA member function call this:
  template(typename aType)
  int & GetInnards(aType &a) { return a.GetA(); }

  // If m_a does not have an GetA() member function, call this:
  template(typename aType)
  int & GetInnards(aType &a) { return a.m_a; }
};

It seems like I should be able to use SFINAE, but I'm missing something.

Also, there is nothing I can test with an #ifdef, so I can't go that route. I need to detect if a method exists, and or if a member is public.

max66
  • 65,235
  • 10
  • 71
  • 111
bpeikes
  • 3,495
  • 9
  • 42
  • 80
  • 2
    Is there a way you can use/access that libraries version number (from a header file or so)? – BitTickler Jun 03 '19 at 20:58
  • 2
    Might be interesting to read : [Check if a class has a member function of a given signature](https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature) – JeJo Jun 03 '19 at 21:00

1 Answers1

3

Well... given a couple of template functions as follows

template <typename A>
constexpr auto hasGetA (A const &)
   -> decltype( std::declval<A>().GetA(), std::true_type{} );

template <typename ... Ts>
constexpr std::false_type hasGetA (Ts const & ...);

you can write your method

  template <typename aType>
  auto GetInnards (aType & a)
     -> std::enable_if_t<true == decltype(hasGetA(a))::value, int &>
   { return a.GetA(); }

  template <typename aType>
  auto GetInnards (aType & a)
     -> std::enable_if_t<false == decltype(hasGetA(a))::value, int &>
   { return a.a; }
max66
  • 65,235
  • 10
  • 71
  • 111
  • why don't you use ``std::enable_if_t<!std::is_member_function_pointer::value,int&>``? I did not get it working myself... and naturally I am curious, why you avoided this. – BitTickler Jun 03 '19 at 23:51
  • Out of curiosity, is there any specific reason why you implemented the test as a `constexpr` function and not as a `struct` overload? – jan.sende Jun 04 '19 at 00:08
  • I think I understand this, but I seem to be having issues in the case where I need to call this on a const B as well as a B, and need to support a const version of GetInnards as well as the non-const one. I seem to be getting errors that compiler can't convert this point to a non-const version of it. I tried adding a CGetInnards(aType const &a) const -> std::enable_if_t – bpeikes Jun 04 '19 at 05:56
  • Ignore my last comment. The issue was that I made the hasGetA functions members of the class B instead of static members or free functions. – bpeikes Jun 04 '19 at 06:56
  • @BitTickler - because your solution works when `GetA()` is present, gives an hard error when `GetA()` miss: when you write `std::is_member_function_pointer::value` (en passant: `aType`, not `T`), you need a pointer to `aType::GetA` to detect that you haven't `aType::GetA`. – max66 Jun 04 '19 at 11:34
  • @jan.sende - no, not a particular reason; and, in this case, I suppose are solutions substantially equivalent (but, in general, with a subtle difference). – max66 Jun 04 '19 at 11:36
  • @max66 Yes, I followed another approach. I did a ``class MyA : public A`` template and wanted to switch between 2 versions: One where the extra ``GetA()`` is being provided and another, which is just plain ``class MyA : public A {}``. But both clang and MSVC19 blather about template alias instantiation errors, which I did not bother to pursue, as I did not have the time budget to figure it out. – BitTickler Jun 04 '19 at 15:12
  • @max66 Mind expanding on those differences? Just the way you write calling code? Or is there a performance benefit? – jan.sende Jun 04 '19 at 21:50
  • @jan.sende - yes, a little difference in the way the code is called: something as `-> std::enable_if_t::value, int &>`; but isn't really important. No, I don't think there is a performance difference; not at run-time, because all the difference is resolved at compile-time (maybe some performance difference at compilation, but no idea which version is favorite). The important difference (in general for this type of solutions, not in this case) is that a function accept also object of derived classes; this isn't true for a type-traits struct/class based. – max66 Jun 05 '19 at 00:10
  • @max66 Ah okay. Thanks! I need a type trait which compiles faster, that's why I'm asking... But the derived classes are a good point! I don't have any right now in my code, but I will keep it in mind for the future. :D – jan.sende Jun 05 '19 at 00:13