Search code examples
c++builder

Incomplete Type error in Builder pattern implementation


I'm playing around the Builder pattern. And, I come across the 'incomplete return type' issue. What should be the correct way to implement it?

// HtmlElement.h

#include <iostream>
#include <string>
using namespace std;

class HtmlBuilder;
class HtmlElement
{
  string name, text;
  HtmlElement()
    {
      cout << "HtmlElement created\n";
    }
 public:
  ~HtmlElement()
    {
      cout << "HtmlElement destroyed\n";
    }

  static HtmlBuilder create();

  friend ostream& operator<<(ostream& os, const HtmlElement& obj)
  {
    return os
      << "name: " << obj.name
      << " text: " << obj.text << endl;
  }

  friend class HtmlBuilder;

};

// HtmlBuilder.h

#include "HtmlElement.h"

using namespace std;
class HtmlBuilder
{
  HtmlElement root;
 public:
  HtmlBuilder()
    {
      root.name = "root";
      root.text = "dummy";
    }
  ~HtmlBuilder()
    {

    }
  HtmlElement build() { return root; }
};

// HtmlElement.cpp

    HtmlBuilder HtmlElement::create()
    {
      return HtmlBuilder();
    }

the compiler is throwing error related to incomplete type.

HtmlElement.cpp:4:33: error: return type 'class HtmlBuilder' is incomplete
4 | HtmlBuilder HtmlElement::create()
  |                                 ^
HtmlElement.cpp: In static member function 'static void HtmlElement::create()':
HtmlElement.cpp:6:22: error: invalid use of incomplete type 'class HtmlBuilder'
6 |   return HtmlBuilder();
  |                      ^
In file included from HtmlElement.cpp:1:
HtmlElement.h:7:7: note: forward declaration of 'class HtmlBuilder'
7 | class HtmlBuilder;

I also tried to have a pointer to HtmlElement class in builder object. But, there was still the same error.


Solution

  • You got into trouble:

    class HtmlElement
    {
    public:
        static HtmlBuilder create(); // requires complete definition of HtmlBuilder
                                     // -> include HtmlBuilder.h
    };
    
    class HtmlBuilder
    {
    public:
        HtmlElement build();         // requires complete definition of HtmlElement
                                     // -> include HtmlBuilder.h
    };
    

    i. e. HtmlElement needs to include HtmlBuilder and vice versa, which is a cyclic include (unavoidable due to cyclic type dependency).

    You need to break this dependency by whatever suitable means. One way could be letting create function return a pointer:

    HtmlBuilder* HtmlElement::create();
    // possibly better a smart pointer:
    std::unique_ptr<HtmlBuilder> HtmlElement::create();
    

    Question, though, is: Why do you need a static creator function at all? What could that function do more than a constructor? If you need to be able to distinguish different construction types, you might do so via tag types (similar to the execution policies used e. g. in std::for_each).