Giter Site home page Giter Site logo

Comments (14)

AMDmi3 avatar AMDmi3 commented on June 2, 2024 1

Full build log please, and which is the actual location of SDL_stdinc.h? Also version of SDL.

from libsdl2pp.

AMDmi3 avatar AMDmi3 commented on June 2, 2024 1

Oh, this is as expected. Your screen library exposes SDL includes through its headers, thus it needs SDL include paths to compile. SDL is partly to blame here for that this doesn't work out of box because official way of including its headers is e.g. #include <SDL_stdinc.h> instead of #include <SDL2/SDL_stdinc.h>, as a result -I/path/to/include/SDL2 is always required. But strictly speaking, dependency include paths should always be specified even if on your system they happen to be included by default.

Build systems such as cmake help with that. If you link your screen library to SDL2pp as e.g. target_link_libraries(screen PUBLIC SDL2pp::SDL2pp) it'll do the thing and pass SDL includes through screen library compile interface.

Alternatively, you may want to avoid exposing SDL2pp/SDL through you library interface. Use forward declarations or pImpl idiom in its headers.

from libsdl2pp.

froart avatar froart commented on June 2, 2024 1

Thanks for help. I added this:
find_package(SDL2pp REQUIRED)
and further as you said:
target_link_libraries(${PROJECT_NAME} PUBLIC SDL2pp::SDL2pp ...
Doesn't complain now.

from libsdl2pp.

AMDmi3 avatar AMDmi3 commented on June 2, 2024 1

Take this library as example:

// foo.h
#include <SDL2pp.hh>
struct MySprite {
    SDL2pp::Texture Texture;
    void Draw(int x, int y);
};
// foo.cpp
#include "foo.h"
MySprite::Draw(int x, int y) {
    Texture.Draw(x, y);
}

This code exposes both internal implementation of MySprite and its dependencies to client code, which now requires SDL2pp compile (and sometimes link) flags for build properly. This negatively affects build system complexity, build times and hinders encapsulation.

There are two general ways to rewrite this code, one using forward declarations:

// foo.h
namespace SDL2pp { class Texture; }
struct MySprite {
    ~MySprite();
    std::unique_ptr<SDL2pp::Texture> Texture;
    void Draw(int x, int y);
};
// foo.cpp
#include "foo.h"
#include <SDL2pp.hh>
MySprite::~MySprite() {
}
MySprite::Draw(int x, int y) {
    Texture->Draw(x, y);
}

Note that dependency is no longer exposed to the client code. Declaration now refers to SDL2pp::Texture as an incomplete type, knowing nothing about it (and thus having to store it as a pointer), and no longer needs to include header with its declaration. Note that you need to define destructor in the source file explicitly, as it needs to be defined where SDL2pp::Texture is a complete type to know how to destruct it properly.

Another way is so called pImpl idiom, where you do basically the same thing, but for whole contents of your class.

// foo.h
struct MySprite {
    struct Impl;
    ~MySprite();
    std::unique_ptr<Impl> pImpl;
    void Draw(int x, int y);
};
// foo.cpp
#include "foo.h"
#include <SDL2pp.hh>

struct MySprint::Impl {
    SDL2pp::Texture Texture;
    void Draw(int x, int y);
}
MySprite::Impl::Draw(int x, int y) {
    Texture.Draw(x, y);
}

MySprite::~MySprite() {
}
MySprite::Draw(int x, int y) {
    pImpl->Draw(x, y);
}

Note that MySprite::Impl is the same as the original class from the very first example, however here it's completely encapsulated in the source file, and none of its internal details or dependencies are exposed through the header. MySprite is a thin wrapper which just forwards all calls to the implementation.

All three approaches have their pros and cons.

  1. Is the simplest, but it exposes internals making it harder to use.
  2. Needs forward declarations for all external types you use.
  3. Needs you to duplicate all public methods.

The most common approach in the wild is 2 - it does not need too much code changes and duplication, and forward declaring a couple of types is usually not a problem at all.

Unfortunately, C++ does not currently provide a tool to avoid all the mentioned drawbacks. While C++ world impatiently awaits modules support, which should finnally fix this.

from libsdl2pp.

AMDmi3 avatar AMDmi3 commented on June 2, 2024 1

Compilation errors you've pasted point to code. The code resides in source files. There's no problem with including SDL2pp headers from the source files. What I've explained above relates to header files only.

from libsdl2pp.

froart avatar froart commented on June 2, 2024

Full build log please, and which is the actual location of SDL_stdinc.h? Also version of SDL.

If you are talking about full build log of my project this is all I got (above). The actual location of SDL_stdinc.h is in /usr/include/SDL2/ . The SDL version is 2.0.20.

To note, when I compile like this surely there are no problems:
g++ test.cpp -lscreen -I/usr/include/SDL2
(screen -- is my library which uses SDL inside)
However, such an approach would always require me to add this include directory to whatever project which uses SDL2pp, which is very inconvenient.

from libsdl2pp.

AMDmi3 avatar AMDmi3 commented on June 2, 2024

If you are talking about full build log of my project this is all I got (above)

I need full command which caused the error

The SDL version is 2.0.20

This is very ancient and not supported. You shouldn't be able to build SDL2pp with it.

Normally, SDL2pp cmakelists propagate SDL includes to targets which link with SDL2pp.

from libsdl2pp.

froart avatar froart commented on June 2, 2024

I need full command which caused the error

$ g++ test.cpp -lscreen
In file included from /usr/local/include/SDL2pp/SDL2pp.hh:34,
from /usr/local/include/screen:6,
from test.cpp:3:
/usr/local/include/SDL2pp/SDL.hh:25:10: fatal error: SDL_stdinc.h: No such file or directory
25 | #include <SDL_stdinc.h>
| ^~~~~~~~~~~~~~
compilation terminated.

from libsdl2pp.

froart avatar froart commented on June 2, 2024

Oh, this is as expected. Your screen library exposes SDL includes through its headers, thus it needs SDL include paths to compile. SDL is partly to blame here for that this doesn't work out of box because official way of including its headers is e.g. #include <SDL_stdinc.h> instead of #include <SDL2/SDL_stdinc.h>, as a result -I/path/to/include/SDL2 is always required. But strictly speaking, dependency include paths should always be specified even if on your system they happen to be included by default.

Build systems such as cmake help with that. If you link your screen library to SDL2pp as e.g. target_link_libraries(screen PUBLIC SDL2pp::SDL2pp) it'll do the thing and pass SDL includes through screen library compile interface.

Alternatively, you may want to avoid exposing SDL2pp/SDL through you library interface. Use forward declarations or pImpl idiom in its headers.

Hi, again. Can I ask you to help with forward declarations? Give me some example of what you mean by that and how this will resolve the issue of exposing header files? I know what the forward declaration is, but how to use it to help to avoid using SDL header files is unclear. Thanks.

And P.S. why do you need headers then, if they are made to deliver you from the headache of declaring everything yourself? I just don't get it.

from libsdl2pp.

froart avatar froart commented on June 2, 2024

@AMDmi3 Hey, thank you for such an extensive reply. I was struggling to find a decent explanation yesterday on the web, yours just killed it.

I was trying to implement the second approach, and I managed to cover the SDL2pp part like this:

typedef uint8_t  Uint8;
typedef uint32_t Uint32;

namespace SDL2pp 
{

  using namespace std;
 
  class Window 
  {
    public:
       Window( const string&, unsigned int, unsigned int, const unsigned int&, const unsigned int&, SDL_WindowFlags );
  };

  class Renderer
  {
    public:
      Renderer( Window, int, Uint32 );
      int SetDrawColor( Uint8, Uint8, Uint8, Uint8 );
      int DrawPoint( int, int );
      int Present();
  };
  
  class Exception
  {
    public:
      string GetSDLFunction();
      string GetSDLError();
  };
}

But, first, it took me a while to google all the return and input types of methods to declare things properly. And second, I still get errors for each of the SDL2 constants used in my code:

error: ‘SDL_WindowFlags’ has not been declared
19 | unsigned int, const unsigned int&, const unsigned int&, SDL_WindowFlags );
SDL_KEYDOWN’ was not declared in this scope
73 | else if ( event.type == SDL_KEYDOWN )
| ^~~~~~~~~~~
... error:
SDLK_ESCAPE’ was not declared in this scope
75 | else if ( event.key.keysym.sym == SDLK_ESCAPE )

and etc, etc... Sounds like much work to do, and honestly I am not quite sure how to do it.

from libsdl2pp.

AMDmi3 avatar AMDmi3 commented on June 2, 2024

Sounds like much work to do, and honestly I am not quite sure how to do it.

Of course you don't need to rewrite class implementations, neither full, neither partial. You just need to add forward declarations for SDL2pp classes in your headers instead of including SDL2pp there.

namespace SDL2pp {
    class Window;
    class Renderer;
    class Exception;
}

from libsdl2pp.

froart avatar froart commented on June 2, 2024

@AMDmi3 But what to do with SDL2 constants? How to get rid of the error?

from libsdl2pp.

froart avatar froart commented on June 2, 2024

@AMDmi3 Got it, thanks. I have the last question. How to forward declare STL vector, string and initializer_list? Simply declaring like this:

template<typename T>
class vector;

...gives me a bunch of errors...

from libsdl2pp.

AMDmi3 avatar AMDmi3 commented on June 2, 2024

Don't do it for std types.

from libsdl2pp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.