Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

determine if struct has a member of specific type

Let's say I have a struct Foo and I want to determine if Foo has an int inside of it.

struct Foo { int a; char c; };
has_int<Foo>::value; // should be true

This is the most basic form of what I'd actually want, checking for a specific type:

has_type<Foo, int>::value;

If I knew how to do the above I could transform it to what my end goal is:

has_pointer<Foo>::value; // false
struct Bar { int a; void *b; };
has_pointer<Bar>::value; // true

As for what I've tried, it's hard to get started, the best I can think of is that if I could get a pack of the types contained in a struct, I could write the rest:

template <typename... Ts>
constexpr bool any_is_pointer() noexcept {
    return (... || std::is_pointer_v<Ts>);
}

What I'm asking for seems like it may very well be impossible. I couldn't find a duplicate, but I was surprised that I couldn't so it might be out there.

like image 449
Ryan Haining Avatar asked Nov 10 '22 04:11

Ryan Haining


1 Answers

As others have said, what you want is impossible right now with vanilla C++ for arbitrary types. But if you are able to provide certain compile-time information alongside the types you need to operate on in their definition, you can do what you are trying to do.

You can do this with the boost fusion library's adapters, which allow you to adapt existing structures to become fusion containers or define new structures which model a fusion container. You can then use any boost::mpl algorithms you want to do the types of compile-time checks you want to do.

Consider this example, using your struct foo, and your desired has_type compile time algorithm:

#include <boost/fusion/adapted/struct/define_struct.hpp>
#include <boost/mpl/contains.hpp>

BOOST_FUSION_DEFINE_STRUCT(
    (your_namespace), foo, 
    (int, a) 
    (char, c))

template<typename source_type, typename search_type>
struct has_type
{
    typedef typename boost::mpl::contains<source_type, search_type>::type value_type;
    static const bool value = value_type::value;
};

#include <iostream>

int main()
{
    bool foo_has_int_pointer = has_type<your_namespace::foo, int*>::value;
    bool foo_has_int = has_type<your_namespace::foo, int>::value;

    std::cout << "foo_has_int_pointer: " << foo_has_int_pointer << "\n";
    std::cout << "foo_has_int: " << foo_has_int << "\n";

    your_namespace::foo my_foo;

    my_foo.a = 10;
    my_foo.c = 'x';

    std::cout << "my_foo: " << my_foo.a << ", " << my_foo.c;
}

View the output or mess with the example here: http://ideone.com/f0Zc2M

As you can see, you use the BOOST_FUSION_DEFINE_STRUCT macro to define the struct whose members you'd like to operate on at compile-time. fusion provides several other macros for defining structures like this, as well as macros for adapting already defined structures. Check them out here.

Of course, you probably can already tell the down-side here. has_type will only work if source_type is a boost::mpl / boost::fusion sequence. For you, this means that any type you want to reason about at compile time in this way, you need to define or adapt using the macros. This may not be acceptable to you if you're writing a library that is intended to work for arbitrary library-user-defined types.

like image 130
bfair Avatar answered Nov 15 '22 13:11

bfair