GithubHelp home page GithubHelp logo

Comments (7)

dmitrykobets-msft avatar dmitrykobets-msft commented on May 30, 2024

Hi @shemeshg, do you have a minimal code example for the case you are describing? That would be very helpful. Thanks.

from gsl.

shemeshg avatar shemeshg commented on May 30, 2024

Here I used //NOLING to suppress the error

You can assume QMenu *fileMenu = new QMenu("File"); is actually gsl::owner<QMenu>("File");
I did not used gsl::owner here because then it will complain I did not freed that memory....

https://github.com/shemeshg/RtMidiWrap/blob/34992346a2fd32b612f231582fe99c77d7bbf08a/src/uimain.cpp

    QMenuBar* menuBar = new QMenuBar(); //NOLINT
    QMenu *fileMenu = new QMenu("File");
    QAction *aboutAct = new QAction(tr("About"), this);
    aboutAct->setStatusTip(tr("Show the application's About box"));
    connect(aboutAct, &QAction::triggered, this, &UiMain::about);

    QAction *aboutQtAct = new QAction(tr("About Qt"), this);
    aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
    connect(aboutQtAct, &QAction::triggered, this, &QApplication::aboutQt);

    QAction *quitQtAct = new QAction(tr("Quit"), this);
    quitQtAct->setStatusTip(tr("Quit"));
    connect(quitQtAct, &QAction::triggered, this, &QApplication::quit);

    fileMenu->addAction(aboutAct);
    fileMenu->addAction(aboutQtAct);
    fileMenu->addAction(quitQtAct);
    menuBar->addMenu(fileMenu);

    mainLayout->setMenuBar(menuBar);

in Qt menuBar is not required to be deleted of freed explicitly, since submitting to parent mainLayout->setMenuBar(menuBar); already does it all., using unique pointer or explicit destructor, would cause it to be deleted twice...

Here example of the QT documentation web site
https://doc.qt.io/qt-6/qtwidgets-mainwindows-menus-example.html#:~:text=the%20QActionGroup%20class.-,MainWindow,-Class%20Implementation

MainWindow::MainWindow()
{
    QWidget *widget = new QWidget;
    setCentralWidget(widget);

    QWidget *topFiller = new QWidget;
    topFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    infoLabel = new QLabel(tr("<i>Choose a menu option, or right-click to "
                              "invoke a context menu</i>"));
    infoLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
    infoLabel->setAlignment(Qt::AlignCenter);

    QWidget *bottomFiller = new QWidget;
    bottomFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->setContentsMargins(5, 5, 5, 5);
    layout->addWidget(topFiller);
    layout->addWidget(infoLabel);
    layout->addWidget(bottomFiller);
    widget->setLayout(layout);

WxWidget example (from ducumentation)
https://docs.wxwidgets.org/latest/overview_helloworld.html#:~:text=with%20respective%20calls.-,MyFrame,-%3A%3AMyFrame()

MyFrame::MyFrame()
        : wxFrame(NULL, wxID_ANY, "Hello World")
{
    wxMenu *menuFile = new wxMenu;
    menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
                     "Help string shown in status bar for this menu item");
    menuFile->AppendSeparator();
    menuFile->Append(wxID_EXIT);
 
    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append(wxID_ABOUT);
 
    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append(menuFile, "&File");
    menuBar->Append(menuHelp, "&Help");
 
    SetMenuBar(menuBar);
 
    CreateStatusBar();
    SetStatusText("Welcome to wxWidgets!");
 
    ... continued below ...

Very simple example easy to compile with cmake and qt

https://code.qt.io/cgit/qt/qtbase.git/tree/examples/widgets/mainwindows/menus?h=6.4

Thanks
Shemeshg

from gsl.

dmitrykobets-msft avatar dmitrykobets-msft commented on May 30, 2024

Hi @shemeshg,

Please confirm if my understanding is correct. You have a couple of distinct concerns here:

You are using an API outside of your control which intends to consume ownership of a pointer, but does not explicitly use the gsl::owner annotation (for instance, QMainWindow::setMenuBar(QMenuBar *menuBar)). You would like to keep using gsl::owner in your own code, and communicate to the ownership checker that this function does take ownership.

You would like for way to transfer function-local ownership into a non-function-local scope, while not invalidating the original owner pointer.

struct A
{
    gsl::owner<int*> class_level_owner;
    void takeOwnership(gsl::owner<int*> owner) { 
        class_level_owner = owner; 
    }
};
void foo(A a)
{
   gsl::owner<int*> my_owner = new int(42);
   a.takeOwnership(my_owner);
   ++(*my_owner); // valid, if we say that my_owner is not "moved"
}
  1. You would like the behavior in (2) to also work for the cases in (1)

from gsl.

shemeshg avatar shemeshg commented on May 30, 2024

Exactly,

This exactly describe the problem.
Especially as you said

++(*my_owner); // valid, if we say that my_owner is not "moved"

is the essence of it, since in QT programmers keeps referencing and changing my_owner while class_level_owner kept private and used for `parental memory management' only..

from gsl.

dmitrykobets-msft avatar dmitrykobets-msft commented on May 30, 2024

Hi @shemeshg,

To address concerns (2) and (3):

With the current model of GSL owner, passing an owner to a function will unavoidably transfer ownership into that function. This applies to both free functions and class methods.

void bar(gsl::owner<int*> o);
struct A
{
    void bar(gsl::owner<int*> o);
};

void foo1()
{
    gsl::owner<int*> ptr = new int(42);
    bar(ptr); // ptr has now transferred ownership into bar's parameter o
    ++(*ptr); // BAD
}
void foo2()
{
    gsl::owner<int*> ptr = new int(42);
    A a;
    a.bar(ptr); // ptr has now transferred ownership into bar's parameter o
    ++(*ptr); // BAD
}

This is the expected behavior, because the ownership model intends for the checks to perform non-global static analysis. The checker will not look into the body of bar in the general case, and so will not attempt to differentiate between implementations of bar such as:

A::bar(gsl::owner<int*> o) { my_class_member_ = o; }
// versus
A::bar(gsl::owner<int*> o) { delete o; }
// - for this implementation, calling `bar(my_owner);` and then `++(*my_owner);` is bad!

So the answer to your question (2) is: no, this is not possible.
If you want to keep using the pointer after it has been passed to the function, you will have to re-acquire it somehow from QMenuBar.

To address concern (1):

Without proper gsl::owner annotations, it is impossible to avoid generating ownership warnings. However, if you want to preserve as many ownership semantics as possible, and minimize the locations where you need to add suppressions, you could create a simple wrapper:

void addMenu(QMenuBar *menuBar, gsl::owner<QMenu*> fileMenu)
{
    // add NOLINT where appropriate
    menuBar->addMenu(fileMenu);
}

This will allow ownership errors to still be caught at the callsites:

// example ownership mistake
void maybeAddFileMenu(QMenuBar* menuBar, bool condition)
{
    gsl::owner<QMenu*> fileMenu = new QMenu("File");
    if (condition) {
        addMenu(menuBar, fileMenu); // ownership transferred in this branch, OK
    }
    // BAD, forgot to delete fileMenu if condition == false
    // owner checker will raise warning
}

If these solutions are not desirable, then the ownership model is most likely not compatible with this existing Qt API.

from gsl.

shemeshg avatar shemeshg commented on May 30, 2024

Hi

Thanks for the detailed response,
The abstraction suggested will not work, since properties in most cases stored on a class.

This would cause the Linter to expects delete in the destructor (and no delete used in Qt).

uimain.h:17:5: member variable of type 'gsl::owner<>' requires the class 'UiMain' to implement a destructor to release the owned resource [cppcoreguidelines-owning-memory]

Thanks anyway, I appreciate very much the effort.
For now I'll just keep using //NOLINT at the new statement, hopefully maybe in the future there will be Owner annotation for that.

from gsl.

dmitrykobets-msft avatar dmitrykobets-msft commented on May 30, 2024

Are you still referring to QMenuBar* menuBar = new QMenuBar(); //NOLINT? Looking at the code in uimain.cpp I think QGridLayout *mainLayout = new QGridLayout; and QMenuBar* menuBar = new QMenuBar(); can use the owner annotation using my suggestions above, since they are not class members. But that is up to you.

from gsl.

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.