0

I have defined the following boost::variant type:

#include <boost/variant.hpp>
#include <vector>
#include <string> 

struct SharedNodeType;
typedef float TypeA;
typedef int TypeB;
typedef std::string TypeC;
typedef char* TypeD;

typedef boost::variant
<TypeA, TypeB, TypeC, TypeD, SharedNodeType> BaseNodeType;

Where TypeA, TypeB, TypeC, and TypeD are fully qualified, complete types (the exact type is irrelevant, but in this case they are other structs).

And I have defined the following struct:

struct SharedNodeType
{
     std::string nodeName;
     BaseNodeType node;
};

However, the struct as written above will not compile, giving pages of errors (far too many to include here), mostly related to "incomplete type not allowed."

The very first compiler error is:

In file included from ./apps/iv_parserTest.cc(1):
/opt/common/boost/1.50.py2.7/include/boost/mpl/sizeof.hpp(27): error: incomplete type is not allowed
 : mpl::size_t< sizeof(T)>

But, if I change the struct to the following, it compiles without a problem:

struct SharedNodeType {
     std::string nodeName;
     std::vector<BaseNodeType> node;
};

Since my SharedNodeType will never have more than one node, this seems wasteful to me.

Why is it that the second form of the struct will compile, but the first form will not?

EDIT: After experimenting some more, I have determined the problem appears to be in the fact that the BaseNodeType variant type also contains SharedNodeType. I have updated my code example accordingly, but it still leaves the question of why the compiler is able to resolve the types if they're inside a std::vector but not otherwise.

sehe
  • 374,641
  • 47
  • 450
  • 633
stix
  • 1,140
  • 13
  • 36
  • 5
    Show a [MCVE] please. – πάντα ῥεῖ Feb 11 '16 at 18:20
  • Are `TypeA, TypeB, TypeC, TypeD` known when you do `BaseNodeType node;`? – NathanOliver Feb 11 '16 at 18:21
  • What are the actual compiler errors that are raised? – Colin Basnett Feb 11 '16 at 18:21
  • @NathanOliver Yes. All of those types are complete. I have updated the code and question to make that clearer. – stix Feb 11 '16 at 18:43
  • [This](http://stackoverflow.com/questions/21743301/c-incomplete-type-not-allowed-error-accessing-class-reference-information-c) may be relevant. – callyalater Feb 11 '16 at 18:44
  • At the time that the compiler encounters the typedef, the SharedNodeType is still incomplete. When that typedef is then included in the SharedNodeType, the variable must be instanceable, but it is not at that point. `std::vector` does not require the template typename to be complete (though it is recommended). See [here](http://en.cppreference.com/w/cpp/container/vector) – callyalater Feb 11 '16 at 18:48
  • @callyalater It doesn't seem there's a simple workaround to this problem other than to hide the BaseNodeType in a std::vector, which has me concerned that I may run into run-time issues. – stix Feb 11 '16 at 18:57
  • @stix If your code is available on a public repo or if a gutted version is on [ideone.com](http://www.ideone.com) or something, I think it would help me better understand what is happening. – callyalater Feb 11 '16 at 19:00
  • I think you can remove all that typedefs, just put one type let's say `int` and then `SharedNodeType`, that should be enough to represent the issue – Slava Feb 11 '16 at 19:03
  • I'm wondering about how the code is decomposed (which parts of code are in which header and source files and how are they included and in what order). This may be the root of the incomplete type issue. – callyalater Feb 11 '16 at 19:07
  • @callyalater The code provided here is the most minimal example complete enough to see the problem. Copying it to a .cc file and compiling it with boost will replicate the issue. Unfortunately I can't provide the entirety of the code. – stix Feb 11 '16 at 21:54
  • @stix If you can't write a proper question, I fear I won't find time to write a proper answer. Here's your answer though: http://www.boost.org/doc/libs/1_60_0/doc/html/variant/tutorial.html#variant.tutorial.recursive – sehe Feb 13 '16 at 14:11
  • @sehe then perhaps you can find the time to explain to me why the question isn't proper? It has the entirety of the code that will not compile, as well as an example that will, and simply asks why one will compile but not the other. I'm not sure what's missing... – stix Feb 15 '16 at 20:20
  • Ok. I thought that was /obviously/ inaccurate. And so went and prove that in 5 minutes, finding that gcc accepts the vector thing indeed. Let me humour you with an answer (other than that, a simple search for "incomplete type" or "recursive variant" should answer it pretty soon) – sehe Feb 15 '16 at 22:18
  • Sorry for going by (the style of) your responses to @callyalater a little bit too much there. Fixed. – sehe Feb 15 '16 at 22:28

1 Answers1

4

Using a type as a member requires it to be complete (obviously, because the size needs to be known).

Therefore there's catch-22 (the size of the variant depends on the size of the SharedNodeType now, and viceversa).

You need to unbreak the dependency cycle.


Accidental solution

You ran into an accidental workaround/solution when you used std::vector<BaseNodeType> intead of BaseNodeType and your particular library implementation graciously/accidentally supports this.

As far as I know this has never been a required feature for standard library containers, and in fact I think I remember that the specifications rather mandate the value_type is complete at the time of instantiation.

Now, you could still use this workaround reliably by switching to Boost Container's vector template (boost::containers::vector<BaseNodeType>), which explicitly does support instantiation with incomplete types.

Surefire Solution

Boost Variant has it's own facility for dealing with incomplete types specifically for creating (potentially) recursive variant types.

Your original sample would look like this, fixed:

Live On Coliru

#include <boost/variant.hpp>
#include <vector>
#include <string> 

struct SharedNodeType;
typedef float       TypeA;
typedef int         TypeB;
typedef std::string TypeC;
typedef char*       TypeD;

typedef boost::variant<TypeA, TypeB, TypeC, TypeD, boost::recursive_wrapper<SharedNodeType> > BaseNodeType;

struct SharedNodeType
{
     std::string nodeName;
     BaseNodeType node;
};

int main() {
    BaseNodeType bnt;
    SharedNodeType snt;
}
sehe
  • 374,641
  • 47
  • 450
  • 633