Click here to Skip to main content
16,022,069 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
I have an embedded graphics library for C++14 and C++17 (preferred) at https://honeythecodewitch.com/gfx

I'm currently working on a major version update so with that comes a lot of refactoring.

I'd currently like to refactor my draw class, which is a class with static template methods on it for doing various drawing operations,

draw::line<>()

draw::filled_ellipse<>()

draw::text<>()

draw::image<>()

etc

The draw class is the primary facility for drawing in my library, so the surface area is bound to be pretty big. I don't mind that. What I'm concerned about is maintenance.

Right now draw:: is several thousand lines of code in a single class. I want to break it up into different classes in different headers, like draw_line, draw_ellipse, draw_polygon etc, and then make draw inherit publicly from all of those.

The issue is what to do with those foundational composition classes? I don't want them to show up in autocomplete lists when you go to draw items. I also want them in different individual files.

One thing I considered was "private headers" that have these classes in them. Those headers could be included in the gfx_drawing.hpp main drawing header and imported under the empty namespace so they only exist for the scope of that master file.
C++
namespace gfx {

namespace {

#include "gfx_draw_line.hpp"

#include "gfx_filled_ellipse.hpp"

...

}

struct draw : public draw_line, public draw_ellipse ... {

....

}

}


I don't like it. For starters I'm concerned about referencing gfx:: qualified items under that nested empty namespace. I'm not sure it will break things, but nor am I sure it won't. Secondly, it feels like preprocessor abuse putting the #includes inside that namespace declaration. Finally, I'm not even sure it will prevent VS Code from making those inner classes show up in intellisense (even if they shouldn't - if they do, I want to name them accordingly as to cut down on actual in practice autocomplete pollution.

I need ideas for hiding these composite classes, or a different way to structure this that allows me to separate drawing operations but combine them together under a public draw:: class <--- that last bit is non-negotiable as changing to something other than my draw:: facilities changes the heart and soul of my library, rendering it very unlike its current self. I'm not ready for that absent some very compelling reasons.

What I have tried:

I'm currently stashing the "hidden" bits under gfx::helpers::

It's less than ideal, but it sort of works

C++
#ifndef HTCW_GFX_DRAW_HPP
#define HTCW_GFX_DRAW_HPP
#include "gfx_draw_common.hpp"
#include "gfx_draw_point.hpp"
#include "gfx_draw_filled_rectangle.hpp"
#include "gfx_draw_line.hpp"
#include "gfx_draw_rectangle.hpp"
#include "gfx_draw_bitmap.hpp"
#include "gfx_draw_icon.hpp"
#include "gfx_draw_text.hpp"
#include "gfx_draw_ellipse.hpp"
#include "gfx_draw_arc.hpp"
#include "gfx_draw_rounded_rectangle.hpp"
#include "gfx_draw_filled_rounded_rectangle.hpp"
#include "gfx_draw_polygon.hpp"
#include "gfx_draw_filled_polygon.hpp"
#include "gfx_draw_sprite.hpp"
#include "gfx_draw_image.hpp"
namespace gfx {
struct draw : public helpers::xdraw_point, 
            public helpers::xdraw_filled_rectangle, 
            public helpers::xdraw_line, 
            public helpers::xdraw_rectangle,
            public helpers::xdraw_bitmap,
            public helpers::xdraw_icon,
            public helpers::xdraw_text,
            public helpers::xdraw_ellipse,
            public helpers::xdraw_arc,
            public helpers::xdraw_rounded_rectangle,
            public helpers::xdraw_filled_rounded_rectangle,
            public helpers::xdraw_polygon,
            public helpers::xdraw_filled_polygon,
            public helpers::xdraw_sprite,
            public helpers::xdraw_image
{};
}
#endif
Posted
Updated 26-Aug-24 7:12am
v2
Comments
Peter_in_2780 25-Aug-24 3:00am    
Might be totally useless, but have you considered an abstract? "drawable" class, inherited by the various shapes, with a common "draw" method overriden by each?
honey the codewitch 25-Aug-24 3:03am    
It doesn't work in this case. The library is stateless, so that would break that, and also fundamentally change the nature of how it works. Furthermore, they'd have to be templated classes anyway due to the abstractions I use to make different binary pixel formats work. I do have a stateful library that builds on it with "controls" that are stateful and draw themselves, but that's a whole different animal.
charles henington 25-Aug-24 17:50pm    
How about

using System.ComponentModel;
[EditorBrowsable(EditorBrowsableState.Never)]
draw::line<>()

It should make those hidden outside of your Assembly
honey the codewitch 25-Aug-24 17:52pm    
I'm going to assume you're joking, as my question is clearly regarding C++, and I did not say anything about .NET. In fact, I mentioned embedded.
charles henington 25-Aug-24 18:29pm    
I use c#, I assumed that c++ had an equivalent. A google search has shown that it doesn't. perhaps this link will be of some use

https://stackoverflow.com/questions/4908539/a-way-in-c-to-hide-a-specific-function

Hi, you can utilize "Policy-Based Class Design" decribed by Alexandrescu, Andrei in his Modern C++ Design: Generic Programming and Design Patterns Applied. His book geared specifically for the library writers.

It may look something like this oversimplified:

C++
template <typename Policy1, typename Policy2, typename Policy3, typename Policy4>
class Implementor
{
public:
    void MakeItHappen()
    {
        Policy1 a;
        Policy2 b;
        Policy3 c;

        a.DoIt();
        b.DoIt();
        c.DoIt();

    }
};


This way you can swap out "Policies" one for another without altering your implementor.
 
Share this answer
 
Comments
honey the codewitch 26-Aug-24 13:11pm    
That's interesting but I'm not sure how it gets me to hiding the parts
Some private headers arent the solution, you should create some
C++
Drawable
interface which provides the needed functions for drawing, debugging, logging and use it as base interface or class to draw your different elements as class objects.
This has also the advantage that you can create some array or vector of these objects or check whether a bigger object is over a smaller one.

No class should have too much functions, a good indicator are 1000 lines of code. Smaller classes are far better maintainable and understandable.
Do you have any plans about testability?

Tip: try to use some libraries for avoiding boilerplate coding but having some bugs in that too. Like some std:string or std:vector.
 
Share this answer
 
Comments
honey the codewitch 25-Aug-24 14:02pm    
The library is stateless. Creating interfaces breaks that, and they'd have to be templates anyway to support the abstractions I'm using.

I've broken them off into separate static classes with template methods. No base interfaces.

But that doesn't solve the hiding problem, and your solution did not address that, despite it being my primary concern.

Instead, you've simply suggested I redesign everything which is not helpful, particularly since you don't have any of the details.
KarstenK 26-Aug-24 5:13am    
You can also use some base class. Only using static classes isnt the best way because you have data which can represented as objects.
My solution is to break down the code in smaller and easier maintainable structures. These needs only some functions and is some "hiding". Like a diameter function has only a circle, but an area function has every 2D object.
honey the codewitch 26-Aug-24 9:16am    
Using objects isn't the best way, because it's less efficient, and adds zero value. It even makes the code harder to use.

You keep talking what's "best" for my code like you know something about it. You don't. You've never even seen it.
KarstenK 27-Aug-24 3:43am    
Now I have seen your struct draw : public helpers::xdraw_point. It is totally top-down of my advice. Now I understand your problem: you have to learn about software architecture at first. Find some tutorial - it is worth the time.
honey the codewitch 27-Aug-24 3:52am    
Your comment is stupid. You didn't name the design pattern I was violating. I think you just want attention. I also think you have a high opinion of yourself you didn't do anything to earn.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900