I did a shadow thing

Are (apparently) shady things ever acceptable for practical reasons?

First, some code on my code. I am writing a graphics module for my 2D game. My module contains more than two classes, but I will focus only on two: Font and GraphicsRenderer.

The font provides an interface with which you can download (and release) files and nothing more. In my Font header, I don't want any implementation details to leak, and this includes the data types of the third-party library that I use. The way I prevent the third-party library from being visible in the header goes through an incomplete type (I understand that this is standard practice):

class Font
{
  private:
    struct FontData;
    boost::shared_ptr<FontData> data_;
};

GraphicsRenderer is a device (read: singleton) that initializes and exits a third-party graphics library, and is also used to render graphic objects (such as fonts, images, etc.). The reason for this is because, as I said, the class initializes a third-party library automatically; it does this when the singleton object is created and exits the library when the singleton is destroyed.

In any case, in order for GR to render Font, it must have access to its FontData object. One option would be to have a public getter, but this would provide an implementation of Font (no other class except Font and GR should care about FontData). Instead, I found it better to make GR a font friend.

. , (singleton friend), , . , , , GR , , , , .

. , GR Font:: data_, , , FontData ( , )? , ...

// =============================================================================
//   graphics/font.cpp
// -----------------------------------------------------------------------------

struct Font::FontData
    : public sf::Font
{
    // Just a synonym of sf::Font
};

// A redefinition of FontData exists in GraphicsRenderer::printText(),
// which will have to be modified as well if this definition is modified.
// (The redefinition is called FontDataSurogate.)
// Why not have FontData defined only once in a separate header:
// If the definition of FontData changes, most likely printText() text will
// have to be altered also regardless. Considering that and also that FontData
// has (and should have) a very simple definition, a separate header was
// considered too much of an overhead and of little practical advantage.


// =============================================================================
//   graphics/graphics_renderer.cpp
// -----------------------------------------------------------------------------

void GraphicsRenderer::printText(const Font& fnt /* ... */)
{
    struct FontDataSurogate
        : public sf::Font {
    };

    FontDataSurogate* suro = (FontDataSurogate*)fnt.data_.get();
    sf::Font& font = (sf::Font)(*suro);

    // ...
}

, . , , , , , , - , , , .:) ( , , .)

+5
4

, - , , , . - (, "", - ).

, , - :

struct FontDataSurogate
    : public sf::Font {
};

, ( ). , , , , , , .

, FontDataSurogate, ( / sf::Font) . , FontDataSurogate, ( - , ).

, ​​ ( ).

friend get , , , /, .

Edit: , , .

" " - , , ..

. , , . , , . #include, ?

" " - printText() , FontData , .

- , ( ). . , .

+3

friend , . . ++ Lite : ?

, undefined : FontDataSurogate* suro = (FontDataSurogate*)fnt.data_.get();

+2

FontData, : Font GraphicsRenderer. . .

, , , . , , - eeevil, , , . , .

- . , GraphicsRenderer, Font. :

class Font
{
  public:
    void do_something_with_fontdata(GraphicsRenderer& gr);

  private:
    struct FontData;
    boost::shared_ptr<FontData> data_;
};

void GraphicsRenderer::printText(const Font& fnt /* ... */)
{
   fnt.do_something_with_fontdata(*this);
}

, Font, GraphicsRenderer . friend ( , ).

, , . , FontData Font, GraphicsRenderer.

0

, , , , , .

, :

, 2 3 . , , , . .

, , - include. . . (, , ), .

:

  • , .
  • Someday someone will change the FontData class without changing PrintText (), maybe they should change PrintText (), but they either haven't done it yet, or don’t know what they need. Or perhaps it’s so that it’s just not related to the extra data in FontData. Regardless of the fact that different pieces of code will work on different assumptions and will be destroyed very quickly.
0
source

All Articles