With help from this question, I have gradually built up this code, a wrapper around a class member function. The idea is that I can use operator* on my property to write to the class object:
#include <cstdio>
#include <type_traits>
#include <utility>
using namespace std;
class testclass {
public:
double get() { return d_; }
bool set(double d) { d_ = d; return true; }
double d_ = 0.0;
};
template <typename PropertyType>
struct DEFAULT_WRAPPER_PROXY {
DEFAULT_WRAPPER_PROXY(PropertyType* p) : property_(p) {}
operator typename PropertyType::GetterReturnType() {
return property_->Get();
}
DEFAULT_WRAPPER_PROXY& operator=(typename PropertyType::GetterReturnType val) {
property_->Set(val);
return *this;
}
PropertyType* property_;
};
template <typename PropertyType>
struct LOGGING_WRAPPER_PROXY {
LOGGING_WRAPPER_PROXY(PropertyType* p) : property_(p) {}
operator typename PropertyType::GetterReturnType() {
// Log some interesting stuff
return property_->Get();
}
LOGGING_WRAPPER_PROXY& operator=(typename PropertyType::GetterReturnType val) {
// Log some interesting stuff
property_->Set(val);
return *this;
}
PropertyType* property_;
};
template<typename Retriever, typename Updater, typename OwningClass, template<typename PropertyType> class WRAPPER_PROXY = DEFAULT_WRAPPER_PROXY>
struct Wrapper {
Wrapper(Retriever retriever, Updater updater, OwningClass* owner) : retriever_(retriever), updater_(updater), containingClass_(owner) {}
using GetterReturnType = std::invoke_result_t<Retriever, OwningClass>;
GetterReturnType Get() { return (containingClass_->*retriever_)(); }
template<typename...Args>
using SetterReturnType = std::invoke_result_t<Updater, OwningClass, Args...>;
template<typename...Args>
SetterReturnType<Args...> Set(Args&&... args) { return (containingClass_->*updater_)((forward<Args>(args))...); }
WRAPPER_PROXY<Wrapper<Retriever, Updater, OwningClass>> operator*() {
return WRAPPER_PROXY(this);
}
Retriever retriever_;
Updater updater_;
OwningClass* containingClass_;
};
template<
template<typename PropertyType>
class WRAPPER_PROXY,
typename Retriever,
typename Updater,
typename OwningClass>
auto MakeWrapper(Retriever&& retriever, Updater&& updater, OwningClass* const owner) {
return Wrapper<Retriever, Updater, OwningClass, WRAPPER_PROXY>(
std::forward<Retriever>(retriever),
std::forward<Updater>(updater),
owner
);
}
int main() {
testclass tc;
// Cleaner (using CTAD here, could also use auto)
Wrapper pp3 = MakeWrapper<LOGGING_WRAPPER_PROXY>(
&testclass::get,
&testclass::set,
&tc
);
*pp3 = 100;
Wrapper pp4 = MakeWrapper<DEFAULT_WRAPPER_PROXY>(
&testclass::get,
&testclass::set,
&tc
);
*pp4 = 100;
}
This does not compile on *pp3 = 100
, because VS2022 states
cannot convert from 'LOGGING_WRAPPER_PROXY<Wrapper<double (__cdecl testclass::* )(void),bool (__cdecl testclass::* )(double),testclass,LOGGING_WRAPPER_PROXY>>'
to 'LOGGING_WRAPPER_PROXY<Wrapper<Retriever,Updater,OwningClass,DEFAULT_WRAPPER_PROXY>>'
with
[
Retriever=double (__cdecl testclass::* )(void),
Updater=bool (__cdecl testclass::* )(double),
OwningClass=testclass
]
I don't understand why it is trying to create a DEFAULT_WRAPPER_PROXY, what is going wrong here please?
CodePudding user response:
The cause of the problem:
struct Wrapper
is a struct with 4 template parameters, and the last of them has a default:
template<typename Retriever,
typename Updater,
typename OwningClass,
template<typename PropertyType> class WRAPPER_PROXY = DEFAULT_WRAPPER_PROXY>
struct Wrapper { /* ... */ }
In this line in struct Wrapper
:
WRAPPER_PROXY<Wrapper<Retriever, Updater, OwningClass>> operator*() {
You don't specify the last template parameter of Wrapper
, and therefore the default of DEFAULT_WRAPPER_PROXY
is used.
This will cause a compilation error when trying to use operator*
with the result of MakeWrapper<LOGGING_WRAPPER_PROXY>
.
The solution:
You can fix it as shown below by adding the last template argument explicitly:
WRAPPER_PROXY<Wrapper<Retriever, Updater, OwningClass, WRAPPER_PROXY>> operator*() {