C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Extreme Template Parameter Possibilities

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
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

Received on 2022-11-29 15:33:58