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.
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).