C++ 如何安全地将包含的对象(菜单项)与需要容器(菜单)的API同步?

C++ 如何安全地将包含的对象(菜单项)与需要容器(菜单)的API同步?,c++,oop,design-patterns,synchronization,wrapper,C++,Oop,Design Patterns,Synchronization,Wrapper,出于我自己的高兴,我正试图想出一个好办法,使使用WindowsAPI菜单的方法更简单。虽然我确实发现了菜单项的类型,并且可能会根据类别来区分,但我还是倾向于这样: 说明 class MenuItem {...}; class MenuBar { //for the main menu of a window (file, help, etc) std::list<MenuItem> items; //and whatever else }; class PopupMen

出于我自己的高兴,我正试图想出一个好办法,使使用WindowsAPI菜单的方法更简单。虽然我确实发现了菜单项的类型,并且可能会根据类别来区分,但我还是倾向于这样:


说明

class MenuItem {...};

class MenuBar { //for the main menu of a window (file, help, etc)
    std::list<MenuItem> items; //and whatever else
};

class PopupMenu { //for dropdown (and possibly context) menus
    std::list<MenuItem> items; //and whatever else
};

问题

问题是,我希望这会改变实际菜单上的菜单项,但不会。我需要某种方式来了解拥有该项目的菜单,因为每个项目都有内部存储的ID

我可以在
MenuBar
中的适当插入函数中执行此操作:

bool MenuBar::someInsertionFunction(unsigned index, MenuItem newItem) {
    newItem.setOwner(*this); //owner as a raw HMENU type, with *this converting
    items.emplace_back(index, newItem); //index checked, of course
}
完成后,我必须让
MenuItem
中的每个setter检查以确保所有者有效,如果有效,则使用API函数更新该项。同样,在getter中,我将调用API函数来获取当前状态,但前提是所有者有效。这允许用户创建自己的
MenuItem
s列表,并通过该列表初始化
菜单。由于
MenuItem
类在保护自身方面做得很好,因此此方法还允许用户完全访问修改内部项列表而不产生任何后果


但这与我发现的一个好概念背道而驰:为什么包含的对象应该知道包含它们的内容?是否有一种设计模式可以解决这个问题,或者我的最佳选择是打破这个“规则”,以便能够让菜单项控制自己(以及其他菜单项),实际上,我没有被菜单控制,而是想出了一个我非常喜欢的答案。它结合了允许菜单项自行更改的功能,同时仍对其他项保持一定的保护

首先,
MenuItem
存储一个函数来更改自身:

std::function<BOOL(UINT, LPMENUITEMINFO)> changeRealItem;
现在,在
MenuItemClass
中,我基本上可以这样做:

MENUITEMINFO info = *this; //passing *this as an HMENU causes address problems
changeRealItem(id(), &info);
作为概念证明,我制作了一个
MessageBox
示例:

#include <functional>
#include <windows.h>

template<typename Ret, typename... Args>
std::function<Ret(Args...)> NonWinapiFunction(Ret(*WINAPI func)(Args...)) {
    return std::function<Ret(Args...)>(func);
}

struct MenuItem {
    MenuItem(std::function<int(const char *)> func) : message(func){}

    void changeSomething(const char *text) const {
        message(text);
    }

private:
    std::function<int(const char *)> message;
};

struct Menu {
    Menu() : item(std::bind(NonWinapiFunction(MessageBox), nullptr, std::placeholders::_1, "Title", MB_OK)){}
    MenuItem &getItem() {
        return item;
    }

private:
    MenuItem item;
};

int main() {
    Menu menu;
    menu.getItem().changeSomething("I can't change other menu items!");
}
#包括
#包括
模板
std::函数非WINAPI函数(Ret(*WINAPI func)(参数…){
返回std::函数(func);
}
结构菜单项{
MenuItem(std::function func):消息(func){}
void changeSomething(常量字符*文本)常量{
信息(文本);
}
私人:
std::函数消息;
};
结构菜单{
菜单():项(std::bind(nonwinapiffunction(MessageBox),nullptr,std::占位符::_1,“Title”,MB_OK)){}
MenuItem&getItem(){
退货项目;
}
私人:
菜单项;
};
int main(){
菜单;
menu.getItem().changeSomething(“我不能更改其他菜单项!”);
}
最后一位是关于
非WinapiFunction
。问题是不能使用
WINAPI
\uu stdcall
)调用约定对函数调用
std::bind
。为了避免这种情况,使用可变模板从函数中提取返回类型和参数类型,并返回具有相同签名但具有正确调用约定的
std::function
,然后可与
std::bind
一起使用


另一个问题是,可以将任意ID传递到函数中,并且需要额外的行来传递winapi结构的地址。我相信这两个问题都可以用一种通用的方法解决(只要存在从包装到包装类型的转换操作符),但我还没有完全弄清楚。实际上,我找到了一个我非常喜欢的答案。它结合了允许菜单项自行更改的功能,同时仍对其他项保持一定的保护

首先,
MenuItem
存储一个函数来更改自身:

std::function<BOOL(UINT, LPMENUITEMINFO)> changeRealItem;
现在,在
MenuItemClass
中,我基本上可以这样做:

MENUITEMINFO info = *this; //passing *this as an HMENU causes address problems
changeRealItem(id(), &info);
作为概念证明,我制作了一个
MessageBox
示例:

#include <functional>
#include <windows.h>

template<typename Ret, typename... Args>
std::function<Ret(Args...)> NonWinapiFunction(Ret(*WINAPI func)(Args...)) {
    return std::function<Ret(Args...)>(func);
}

struct MenuItem {
    MenuItem(std::function<int(const char *)> func) : message(func){}

    void changeSomething(const char *text) const {
        message(text);
    }

private:
    std::function<int(const char *)> message;
};

struct Menu {
    Menu() : item(std::bind(NonWinapiFunction(MessageBox), nullptr, std::placeholders::_1, "Title", MB_OK)){}
    MenuItem &getItem() {
        return item;
    }

private:
    MenuItem item;
};

int main() {
    Menu menu;
    menu.getItem().changeSomething("I can't change other menu items!");
}
#包括
#包括
模板
std::函数非WINAPI函数(Ret(*WINAPI func)(参数…){
返回std::函数(func);
}
结构菜单项{
MenuItem(std::function func):消息(func){}
void changeSomething(常量字符*文本)常量{
信息(文本);
}
私人:
std::函数消息;
};
结构菜单{
菜单():项(std::bind(nonwinapiffunction(MessageBox),nullptr,std::占位符::_1,“Title”,MB_OK)){}
MenuItem&getItem(){
退货项目;
}
私人:
菜单项;
};
int main(){
菜单;
menu.getItem().changeSomething(“我不能更改其他菜单项!”);
}
最后一位是关于
非WinapiFunction
。问题是不能使用
WINAPI
\uu stdcall
)调用约定对函数调用
std::bind
。为了避免这种情况,使用可变模板从函数中提取返回类型和参数类型,并返回具有相同签名但具有正确调用约定的
std::function
,然后可与
std::bind
一起使用

另一个问题是,可以将任意ID传递到函数中,并且需要额外的行来传递winapi结构的地址。我相信这两个问题都可以用通用的方法解决(只要存在从包装器到包装类型的转换操作符),但我还没有完全弄清楚