Search code examples
c++sfmlincomplete-type

SFML RenderTarget.draw : Invalid use of incomplete type


I am reading the book https://www.packtpub.com/game-development/sfml-game-development

On Chapter 3 page 61 we are trying to draw a sprite on a target like this:

void Aircraft::drawCurrent(sf::RenderTarget& target,
sf::RenderStates states) const
{
    target.draw(mSprite, states);
}

Companion code for the book on github is here https://github.com/SFML/SFML-Game-Development-Book/blob/master/03_World/Source/Aircraft.cpp#L31

When I am trying to compile, it gives me this error:

 error: invalid use of incomplete type ‘class sf::RenderTarget’
     target.draw(sprite, states);
     ^~~~~~

I am scratching my head as looking at the documentation, it looks like the right thing to do https://www.sfml-dev.org/documentation/2.4.2/classsf_1_1RenderTarget.php#a12417a3bcc245c41d957b29583556f39

My code following the book is at https://github.com/ishanatmuz/SFMLSnippets/blob/chapter-3/aircraft.cpp#L11


Solution

  • You're tripping over a forward declaration of a type here.

    • A complete type is defined using (for example) class MyTypeName {...};. This gives it a name and defines its actual structure (i.e. members, their type, size, alignment, etc.).

    • An incomplete type is basically just the hull, like class MyTypeName;, only telling the compiler that this name represents a type that is actually a class. Otherwise it wouldn't know how to interpret MyTypeName whenever you use it.

    If you're only using pointers or references for your MyTypeName class, its actual structure/contents are unimportant. As such it's sufficient to use the incomplete type (which is declared in several SFML headers so it won't have to include the full header for the class).

    However, once you start calling members of the type, you'll need the complete type, since the compiler has to know about the memory layout of the actual type. it's no longer enough just to know that it's a class (or an integer, string literal, etc.). To solve this, you actually include the header file with the complete type inside.


    Why even use forward declarations then?

    Multiple reasons, the most prominent ones:

    • Faster. If it's enough for the compiler to know that MyTypeName is a class (e.g. to define a pointer or reference), parsing one single line is enough. It won't have to parse potentially hundreds of lines, store structure information etc. just to throw it away a few lines later.

    • Helps you to implement circular dependencies. Imagine class A having a pointer to an object of class B and vice-versa. You can't define one class after the other, since the compiler won't know what B is when you define A before B.