efficient / libcuckoo

A high-performance, concurrent hash table
Other
1.62k stars 275 forks source link

Compilation failure: "incomplete type used in type trait expression" #108

Closed m-hilgendorf closed 5 years ago

m-hilgendorf commented 6 years ago

Replicating this is trivial:

#include <libcuckoo/cuckoohash_map.hh>
struct Foo
{
        cuckoohash_map<int, Foo> map;
};

The issue is in libcuckoo_bucket_container.hh, where you're using std::is_trivial to enable the istream and ostream operator<< overloads, where at that point Foo is an incomplete type. Commenting out those overloads solves the issue.

Imho a very easy way to patch over the error is to add an option in CMakeLists.txt to disable serialization with preprocessor flags. Afaict there's no way to determine if a type is complete using type traits, at least no good way.

manugoyal-nuro commented 6 years ago

Ah yes it would certainly be nice to support these recursively-defined types if possible. If all it takes is to disable istream/ostream, maybe it would make sense to add another template parameter to our cuckoohash_map IS_INCOMPLETE_TYPE, which we can short-circuit these checks with? I haven't tried this so I don't know whether it would work, but the nice thing is that you'd be able to use the same header for complete and incomplete types.

I am somewhat unclear on what exactly is necessary for maintaining support for recursively-defined types like this, so I'd like to understand better what precautions we'd need to take to support this functionality for the long term. As far as I'm aware, the STL containers do not officially support incomplete types, though it does work for a few data structures.

m-hilgendorf commented 6 years ago

I didn't realize this use case was covered in the examples.... So to anyone else reading this, the workaround is to use a unique_ptr to the map in any type that may be nested in another table.

The issue isn't that the type is incomplete, it's that the type is incomplete when the compiler tries to define std::is_trivial. It's essentially a circular definition problem. Here's a stripped down example of the issue:

template<class T> 
struct Foo
{
    typename std::enable_if <std::is_trivial<T>::value, void>::type
    method () {
        std::cout << "this doesn't compile" << std::endl;
    }
};

struct Bar
{
    Foo <Bar> foo;
}; // <---- Bar is not a complete type until this closing brace

if all it takes is to disable istream/ostream, maybe it would make sense to add another template parameter to our cuckoohash_map IS_INCOMPLETE_TYPE

The problem can be fixed by moving the istream/ostream operator definitions outside the class declaration.

manugoyal commented 5 years ago

I added a commit (cd2aa7a6586afd0b66c5aa2fa157c2a5ed7f0e94) which allows the table to work with incomplete types. I wasn't having an easy time figuring out how to declare these operator<< functions outside the table, because C++ seems to have some restrictions about declaring complicated conditionally-enabled friend functions, so I went for some other approach which also seems to work.