0

I want to have a template function which accepts unary member-function pointers of an instance of some generic type. My problem is that I must support both void(T val) and void(const T& val) member functions.

I have written one template function for each case and it works fine, but this leads to code duplication since the function logic is completely the same. (I found something completely similar here: Function taking both pointer to member-function and pointer to const member-function but I fail to see a definitive solution).

An example of the generic type mentioned above:

using UserAddress = std::string;

class User
{
private:
    int mHeight;
    UserAddress mAddress;
public:
    void SetHeight(int height){mHeight = height;}
    void SetAddress(const UserAddress& address){mAddress = address;}
};

Where UserAddress is some heavy type I want to pass by reference.

My templated function:

template <typename TPersistentObject>
class Persistence
{
private:
    std::map<std::string, std::function<void(User*)>> mSetterOfProperty;

    template <typename TPersistentObject, typename TPropertyValue>
    void DefinePropertySettingMethod(const std::string& propertyName,
                                     void (TPersistentObject::*propertySetter)(TPropertyValue), std::function<TPropertyValue(void)> dataReader)
    {
            mSetterOfProperty[propertyName] =
                [propertySetter, columnDataReader](TPersistentObject* persistentObject) 
            {
                (persistentObject->*propertySetter)(dataReader());
            };
    }
};

/// Const& implementation leading to code duplication
    template <typename TPersistentObject, typename TPropertyValue>
    void DefinePropertySettingMethod(const std::string& propertyName,
                                     void (TPersistentObject::*propertySetter)(const TPropertyValue&), std::function<TPropertyValue(void)> dataReader)
    {
...
    }
};

Is there some way to define this function to support the following:


int main()
{
    
    auto  intDataReader = []() {
        return 1;
    };
    
    auto  stringDataReader = []() {
        return UserAddress("Next Door");
    };
    
  Persistence p;
  p.DefinePropertySettingMethod<User,int>("Height", &User::SetHeight, intDataReader);
  p.DefinePropertySettingMethod<User,UserAddress>("Address", &User::SetAddress, stringDataReader);
   
}
6
  • This close approximation to your code compiles with just the first overload. It's not clear why you feel you need both. Commented Oct 12, 2019 at 3:36
  • I hadn't included a third parameter on the function. I am sorry. I have modified the code. Now you should be able to get the error. I also see the problem now (the const& substitution on the type of the third parameter), but the question still stands. Maybe some trait (I am not familiar with traits). Commented Oct 12, 2019 at 17:15
  • Have two separate template parameters in place of TPropertyValue - one for setter, one for getter. Allow them to be deduced independently. Commented Oct 12, 2019 at 17:52
  • This worked nicely (I don't even need the explicit type declarations now). Thank you!. However it loosens the type dependency between the type passed to the setter function and the type returned from the dataReader. In the function I invoke the dataReader and I pass its return value in the setter. The compiler will complain if there is a type mismatch but for clarity reasons, I'd like to have i this dependency expressed on the function prototype. Commented Oct 13, 2019 at 6:47
  • You simply can static_assert on a type mismatch... Commented Oct 13, 2019 at 7:36

1 Answer 1

0

Thanks to Igor Tandetnik 's tip I managed to compile a solution. std::enable_if is not what I needed though since I did not need to deactivate an overload (or at least I couldn't come to a solution using it). std::conditional did the trick.

Here is the code:

#include <string>
#include <functional>
#include <map>
#include <string>
#include <type_traits>

using UserAddress = std::string;

class User
{
private:
    int mHeight;
    UserAddress mAddress;
public:
    void SetHeight(int height){mHeight = height;}
    void SetAddress(const UserAddress& address){mAddress = address;}
};

template <typename TPersistentObject>
class Persistence
{
public:
    std::map<std::string, std::function<void(TPersistentObject*)>> mSetterOfProperty;

    template <typename TPropertyValue>    
    void DefinePropertySettingMethod(const std::string& propertyName,
                                     void (TPersistentObject::*propertySetter)(TPropertyValue), 
                                     std::function<
                                             typename std::conditional<!std::is_same<TPropertyValue, typename  std::decay<TPropertyValue>::type>::value,
                                             typename std::decay<TPropertyValue>::type, TPropertyValue>::type
                                     (void)> dataReader)
    {
            mSetterOfProperty[propertyName] =
                [propertySetter, dataReader](TPersistentObject* persistentObject) 
            {
                (persistentObject->*propertySetter)(dataReader());
            };
    }
};


int main()
{

    std::function<int()>  intDataReader = []() {
        return 1;
    };

    std::function<std::string()>  stringDataReader = []() {
        return UserAddress("Next Door");
    };

  Persistence<User> p;
  p.DefinePropertySettingMethod("Height", &User::SetHeight, intDataReader);
  p.DefinePropertySettingMethod("Address", &User::SetAddress, stringDataReader);

}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.