Date: Tue, 29 Nov 2022 10:33:45 -0500
On Tue, Nov 29, 2022 at 7:09 AM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:
> On Tue, Nov 29, 2022 at 10:29 AM Frederick Virchanza Gotham <
> cauldwell.thomas_at_[hidden]> wrote:
> >
> > template<text K, typename T>
> > constexpr auto GetRelevantMethodPointer(T const *const p)
> > {
> > if constexpr ( false == std::is_class_v<T> ) return
> > static_cast<void(*)(void)>(nullptr);
> > else if constexpr ( std::derived_from<T,std::istream> ) return
> &T::read;
> > else if constexpr ( requires { &T::put; } ) return
> &T::put;
> > else
> > {
> > return &T::K;
> > }
> > }
>
> This wasn't a very good example. A better example would be where we
> want to find a member of the same name in a different class. See line
> #7 in the following:
>
> 00: template<typename T>
> 01: constexpr auto GetRelevantMethodPointer(T const *const p)
> 02: {
> 03: if constexpr ( false == std::is_class_v<T> ) return
> 04: static_cast<void(*)(void)>(nullptr);
> 05: else if constexpr ( std::derived_from<T,std::istream> )
> return &T::read;
> 06: else if constexpr ( requires { &T::put; } ) return &T::put;
> 07: else if constexpr ( requires { &SomeOtherClass::K; } ) return
> &SomeOtherClass::K;
> 08: else
> 09: {
> 10: return &T::K;
> 11: }
> 12: }
>
This doesn't seem like idiomatic C++ code at all. Generally in C++ you need
to stay far away from pointers-to-member, because member functions can be
overloaded, or can be function templates, at which point it's impossible to
get a member pointer to them at all. Prefer to use C++11 lambdas instead.
template<class T, class Fallback>
auto GetReaderFunction(const T *p, Fallback fallback) {
static_assert(std::is_class_v<T>);
if constexpr (std::derived_from<T, std::istream>) {
return [p](char *s, size_t n) { p->read(s, n); };
} else if constexpr (requires (char *s, size_t n) { p->put(s, n); }) {
return [p](char *s, size_t n) { p->put(s, n); };
} else {
return fallback;
}
}
struct Foo {
void put(auto*, size_t) {}
};
struct Bar {
void baz(size_t) {}
};
int main() {
Foo foo;
Bar bar;
auto one = GetReaderFunction(&std::cin, 0);
auto two = GetReaderFunction(&foo, 0);
auto three = GetReaderFunction(&bar, [&bar](char*, size_t n) {
bar.baz(n); });
}
Looking at the call-sites in `main`, you quickly realize that there's no
reason to ever pass `fallback` — it will be known statically at compile
time whether that parameter is meaningful or not, and if it *is* meaningful
then there's literally no reason to call `GetReaderFunction` in the first
place. So this code can continue to be simplified beyond what I've done
here.
–Arthur
Std-Proposals <std-proposals_at_[hidden]> wrote:
> On Tue, Nov 29, 2022 at 10:29 AM Frederick Virchanza Gotham <
> cauldwell.thomas_at_[hidden]> wrote:
> >
> > template<text K, typename T>
> > constexpr auto GetRelevantMethodPointer(T const *const p)
> > {
> > if constexpr ( false == std::is_class_v<T> ) return
> > static_cast<void(*)(void)>(nullptr);
> > else if constexpr ( std::derived_from<T,std::istream> ) return
> &T::read;
> > else if constexpr ( requires { &T::put; } ) return
> &T::put;
> > else
> > {
> > return &T::K;
> > }
> > }
>
> This wasn't a very good example. A better example would be where we
> want to find a member of the same name in a different class. See line
> #7 in the following:
>
> 00: template<typename T>
> 01: constexpr auto GetRelevantMethodPointer(T const *const p)
> 02: {
> 03: if constexpr ( false == std::is_class_v<T> ) return
> 04: static_cast<void(*)(void)>(nullptr);
> 05: else if constexpr ( std::derived_from<T,std::istream> )
> return &T::read;
> 06: else if constexpr ( requires { &T::put; } ) return &T::put;
> 07: else if constexpr ( requires { &SomeOtherClass::K; } ) return
> &SomeOtherClass::K;
> 08: else
> 09: {
> 10: return &T::K;
> 11: }
> 12: }
>
This doesn't seem like idiomatic C++ code at all. Generally in C++ you need
to stay far away from pointers-to-member, because member functions can be
overloaded, or can be function templates, at which point it's impossible to
get a member pointer to them at all. Prefer to use C++11 lambdas instead.
template<class T, class Fallback>
auto GetReaderFunction(const T *p, Fallback fallback) {
static_assert(std::is_class_v<T>);
if constexpr (std::derived_from<T, std::istream>) {
return [p](char *s, size_t n) { p->read(s, n); };
} else if constexpr (requires (char *s, size_t n) { p->put(s, n); }) {
return [p](char *s, size_t n) { p->put(s, n); };
} else {
return fallback;
}
}
struct Foo {
void put(auto*, size_t) {}
};
struct Bar {
void baz(size_t) {}
};
int main() {
Foo foo;
Bar bar;
auto one = GetReaderFunction(&std::cin, 0);
auto two = GetReaderFunction(&foo, 0);
auto three = GetReaderFunction(&bar, [&bar](char*, size_t n) {
bar.baz(n); });
}
Looking at the call-sites in `main`, you quickly realize that there's no
reason to ever pass `fallback` — it will be known statically at compile
time whether that parameter is meaningful or not, and if it *is* meaningful
then there's literally no reason to call `GetReaderFunction` in the first
place. So this code can continue to be simplified beyond what I've done
here.
–Arthur
Received on 2022-11-29 15:33:58