Question

I'm currently trying to make a simple program with UI where a user can add, select and remove different objects inherited from an abstract common interface with a mouse.

What I plan to do is use a set<unique_ptr<Concrete_class*>> to store collections separately for each type of all the objects created and store one currently selected object in a unique_ptr<Abstract_class*>.

The problem is that if I want to delete the selected object there is no elegant way to do it: either I have to use a combination of .get(), dynamic casts and if/else chains to ensure the object is removed from the right set or store the selected object in a variant<Concrete_class1*, Concrete_class2*, ...> which makes using polymorphism pointless. It's also impossible to store all the objects in one set since I plan to use their specific functions.

Is there a better way to approach this problem? I use dear imgui+sfml if that's important.

Était-ce utile?

La solution

You've got two levels of indirection too many in your collections, and you really shouldn't have two std::unique_ptr's pointing to the same object.

You can store how to erase the element when you select it.

class Container {
    std::set<ConcreteClass1> concrete1;
    std::set<ConcreteClass2> concrete2;
    AbstractClass * selected;
    std::function<void()> remover;
public:
    // Or some other way of identifying an element of concrete1
    void select(std::set<ConcreteClass1>::iterator it) {
        selected = &*it;
        remover = [=](){ concrete1.erase(it); selected = nullptr; };
    }

    // Or some other way of identifying an element of concrete2
    void select(std::set<ConcreteClass2>::iterator it) {
        selected = &*it;
        remover = [=](){ concrete2.erase(it); selected = nullptr; };
    }

    void removeSelected() {
        remover();
    }
};

Autres conseils

You shouldn't have a set of ConcreteClass, you should use a set of AbstractClass and use the heap (new/delete).

First off, keep in mind that GUI structure (e.g., for rendering) generally doesn't match the logical structure of what the program itself does.

For example, if you have a list of items in your GUI, you presumably also have some logical component in your program that gives that list some sort of semantics within the program. The reason that the user adds an item to the list will almost certainly differ from the reason that the GUI framework needs to know that the item belongs to the list.

The point is that you will pretty much never have a program where the inheritance and nesting of GUI components reflects the semantics of the corresponding program objects. The GUI should be an expression of information rather than the information.

One approach is to have each renderable object own its GUI representation, rather than having the GUI object contain the program semantics. (Also see Composition over inheritance.)

For example, suppose you wanted to implement a list of filenames, where the user can create, open, or delete a file.

  • class FilenameList:
    • Contains a pointer to its GUI widget that can be passed to its parent for rendering.
    • Contains a list of Filename.
    • The create, open, and delete operations perform actions on both the GUI representation and the list of Filename.
    • GUI event handlers are set to delegate create, open and delete actions to the FilenameList object.

With such a representation, it's irrelevant to the GUI framework what the semantics of the program objects are; it just needs to be able to delegate events and call handlers. Then the owner of the GUI component registers itself to handle specific events, e.g., a "delete" button click.

Thus, you end up dealing with "regular" program objects that are able to handle their own GUI representations, rather than structuring the program directly with GUI representations.

Licencié sous: CC-BY-SA avec attribution
scroll top