3

Looking at making a GUI library for a game engine. I generally avoid pointers if I don't need them and in my below example I find that I don't and it works, but just curious if this design is generally considered good practice or if anyone has comments on it.

int main()
{
    // all widgets are positioned and sized according to the window size
    Window wndOptions("wndOptions");

    Button cmdExit("cmdExit");

    // the window doesn't own the widgets nor does it create the widgets so it's not responsible for deleting the widgets
    wndOptions.AddWidget(&cmdExit);

    // this is how you can find a control and get it casted also. make your variable a reference
    Button& test = wndOptions.FindWidget<Button>("cmdExit");

    test.SetPosition(50, 25);

    return 0;
}

As you can see the idea is that you create your controls (button in this case) and pass a pointer to them to the Window. Then you can find the control by name returning a reference to that control in which you can manipulate the control. It's a bit pointless to use FindWidget() in this example since I have the cmdExit variable, but if I was to pass the window around this would be a way to find and use controls.

The biggest issue I have is that FindWidget() might not find a widget. Right now I throw an exception as not finding a control I would deem as a true exception and not something that should happen much if at all.

Any comments about the usage being done this way?

6
  • What happens if I do Button& test = wndOptions.FindWidget<Button>("cmdExitoopsie"); ? What do you return if I ask for something not there?
    – user53141
    Commented May 16, 2014 at 19:18
  • I throw an exception as I would view that as a rare occurrence.
    – user441521
    Commented May 16, 2014 at 19:21
  • 2
    What happens if cmdExit goes out of scope? Commented May 17, 2014 at 9:10
  • @JBRWilkinson Because inside FindWidget() it does a dynamic_cast() it will actually throw an exception saying Bad cast! That seems to be a neat little trick with all of this, which aids in avoiding a dangling pointer. Without the cast to the actual type this would be an issue.
    – user441521
    Commented May 19, 2014 at 13:33
  • @user441521 I doubt this will work. Doing a dynamic_cast on an invalid (dangling) pointer is undefined behavior. It might throw an exception on your specific compiler, platform and build configuration (I guess "debug" in MSVC?). But it's neither guaranteed to nor required to, it might pretty well just crash your program. Commented May 19, 2014 at 14:37

2 Answers 2

3

Looks good so far, although I'm wondering about one thing:

Why would a Window not own the widgets? Seems perfectly reasonable to me, since it already has the capability to store and return them. Who else would own them?

If widgets actually have multiple owners, I'd suggest using std::shared_ptr - it makes the ownership explicit and prevents mistakes like passing pointers to local stack variables etc. In fact, I'd generally use smart pointers as much as possible.

Returning references and throwing exceptions are both good choices in my eyes.

4
  • +1, assuming Window can't own the widgets, shared_ptr is the way to go.
    – VF1
    Commented May 17, 2014 at 1:35
  • The way I see this play out is that these controls are injected into the window, but the window doesn't actually own them, it just uses them. If a person points to a local stack variable and that variable goes out of scope and tries to find it from the window later it'll through and error because of the templated find function that will try to cast to the actual type which throws a bad cast exception. This seems to be a way to get around a dangling pointer from what I can see.
    – user441521
    Commented May 19, 2014 at 13:38
  • 1
    Why would that throw a bad_cast? You only get that with valid pointers whose RTTI doesn't match. It's just undefined behavior otherwise. You cannot "get around" a dangling pointer. By using a std::shared_ptr, you prevent a dangling pointer, and I'd say it's the right thing to do even if the window does not "own" the widgets conceptually. Commented May 19, 2014 at 13:43
  • @user441521: Maybe your first language is not C/C++. Most languages prevent you from doing stupid things, and if you are doing something wrong, you will be notified. C/C++ does not do that and are called unsafe language for this reason. If you have a dangling pointer, all bets are off. You may get a crash or an exception if you are lucky, or the program goes on and corrupt your database, crash somewhere else so that you can never find the real reason, or launch a nuclear missile.
    – Siyuan Ren
    Commented Oct 7, 2014 at 11:41
0

Throwing an exception should mean that a piece of code somewhere catches the exception and handles it in an appropriate manner.

This might mean reporting the error back to the user to fix. Exceptions can be caused by transient situations where the handling mechanism is to try again later.

If it is a programming error that causes the widget not to be found, you might use an exception but an assert is likely to be preferable.

You would use shared_ptr if the widget does have more than one "owner" and clean-up depends on when the last reference is let go. Sometimes shared_ptr is also often used as a convenient way to store the items in a collection as it has the right semantics, even if, in a sense, the collection "owns" it.

If not finding something is a "normal" condition then you would have to return a pointer not a reference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.