There are several ways of doing this using requires
, depending on how strictly you want to check the method.
E.g.:
device_module_info cls{};if constexpr (requires(DeviceHandleImpl h, off_t pos, void *buffer, size_t *_length){ h.Read(pos, buffer, _length);}) cls.read = /*some lambda*/;
This only checks that the method exists and that the argument types are correct. If the return type is wrong, you get a compilation error.
You could check the return type too. This checks for exact match:
if constexpr (requires(DeviceHandleImpl h, off_t pos, void *buffer, size_t *_length) { requires std::same_as<status_t, decltype(h.Read(pos, buffer, _length))>;}) cls.read = /*some lambda*/;
Or this checks for convertibility:
if constexpr (requires(DeviceHandleImpl h, off_t pos, void *buffer, size_t *_length) { { h.Read(pos, buffer, _length) } -> std::convertible_to<status_t>;}) cls.read = /*some lambda*/;
All of the approaches above allow implicit conversions in arguments. I don't think there's an easy way to prevent that.