8
\$\begingroup\$

I wanted to detect if I have a member in a simple POD struct and after some searching and merging some methods I found on the web I've come up with this solution:

#include <iostream>

template<class...Fs>
struct funcs_t{};

template<class F0, class...Fs>
struct funcs_t<F0, Fs...>: F0, funcs_t<Fs...> {
    funcs_t(F0 f0, Fs... fs):
    F0(std::move(f0)), funcs_t<Fs...>(std::move(fs)...)
    {}
    using F0::operator();
    using funcs_t<Fs...>::operator();
};

template<class F>
struct funcs_t<F>:F {
    funcs_t(F f) : F(std::move(f)) {};
    using F::operator();
};

template<class...Fs>
funcs_t<std::decay_t<Fs>...> funcs(Fs&&...fs) {
    return {std::forward<Fs>(fs)...};
}

#define HAS_MEMBER(cls, memb)\
    [](){\
        auto with_memb = [](auto &&A, int x)\
                -> decltype(typename std::remove_reference<decltype(A)>::type().memb, std::true_type())\
        {\
            return std::true_type();\
        };\
        auto with_no_memb = [](auto &&A, float x) -> std::false_type {\
            return std::false_type();\
        };\
        auto has_memb = funcs(with_memb, with_no_memb);\
        return has_memb(cls(), 1);\
    }()

struct A {
    int a;
    int b;
};

int main(int argc, char const *argv[])
{
    std::cout << HAS_MEMBER(A, b) << std::endl;
    return 0;
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ Why do you need this? What are you trying to achieve? \$\endgroup\$ Commented Jul 19, 2019 at 16:39
  • \$\begingroup\$ I have different pod structs with many common fields with the same meaning and I wanted to print them on screen. I will see if I can use it for other things but for now that's what I needed it for. \$\endgroup\$
    – Pangi
    Commented Jul 19, 2019 at 18:45
  • \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. If you want a fixed-version of your code to be available, post it as answer linking to Deduplicator's for credit (or post it as a new question, but it's hardly a major change). \$\endgroup\$
    – Mast
    Commented Jul 19, 2019 at 19:04
  • \$\begingroup\$ Ok, I'll keep that in mind. \$\endgroup\$
    – Pangi
    Commented Jul 19, 2019 at 19:16

1 Answer 1

6
\$\begingroup\$
  1. Your detector fails in the face of overloading and templating. Also, it will only detect (static) member-variables and (static) member-functions. While you can extend it to types (and type-aliases), accepting templates and overloading would need better language-provided reflection-facilities.

    Anyway, pure existence is generally uninteresting, supported operations count.

  2. funcs() and funcs_t are nearly a generally useful abstraction.

    Just use perfect forwarding instead of by-value and std::move(). Allowing for all callables, including final classes, function-pointers, member-function-pointers, and the same wrapped in a std::reference_wrapper would admittedly add significant amounts of code.

    A good name would be overloaded.

  3. HAS_MEMBER needlessly depends on default-constructing the passed class. Fix that by using decltype, std::declval() and unevaluated contexts.

  4. If you don't use an argument, don't name it. Specifically for main(), just don't ask for it.

  5. Don't use std::endl. In the rare cases you actually need to flush manually, be explicit and use std::flush. Nearly always you are just crippling performance.

  6. return 0; is implicit for main(). Make of that what you will.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.