我想在一个集合中存储具有相似签名的函数来做这样的事情:
f(vector<Order>& orders, vector<Function>& functions) {
foreach(process_orders in functions) process_orders(orders);
}
void GiveCoolOrdersToBob(Order);
void GiveStupidOrdersToJohn(Order);
typedef void (*Function)(Order);
vector<Function> functions;
functions.push_back(&GiveStupidOrdersToJohn);
functions.push_back(&GiveCoolOrdersToBob);
struct IOrderFunction {
virtual void operator()(Order) = 0;
}
struct GiveCoolOrdersToBob : IOrderFunction {
...
}
struct GiveStupidOrdersToJohn : IOrderFunction {
...
}
vector<IOrderFunction*> functions;
functions.push_back(new GiveStupidOrdersToJohn());
functions.push_back(new GiveCoolOrdersToBob());
最佳答案
前提:
您提出的设计将有效,但使用常规函数指针将大大限制您可以注册的回调类型,尽管更强大,但基于从固定接口(interface)继承的方法更加冗长,并且需要客户端进行更多工作定义回调。
在这个答案中,我将首先展示一些如何使用 std::function 的示例。以此目的。这些例子几乎不言自明,展示了如何以及为什么使用 std::function与您概述的那种解决方案相比,带来了优势。
然而,一种基于 std::function 的幼稚方法也将有其自身的局限性,我将列出。这就是为什么我最终建议你看看 Boost.Signals2 :它是一个非常强大且易于使用的库。我将在本答案的末尾讨论 Boost.Signals2。希望了解基于 std::function 的简单设计first 将使您在以后更容易掌握信号和插槽的更复杂方面。
基于 std::function<> 的解决方案
让我们介绍几个简单的类,并为一些具体的例子做准备。在这里,一个 order是具有 id 的东西并包含多个 item s。每个item由 type 描述(为简单起见,这里可以是书或 DVD)和 name :
#include <vector>
#include <memory>
#include <string>
struct item // A very simple data structure for modeling order items
{
enum type { book, dvd };
item(type t, std::string const& s) : itemType(t), name(s) { }
type itemType; // The type of the item
std::string name; // The name of the item
};
struct order // An order has an ID and contains a certain number of items
{
order(int id) : id(id) { }
int get_id() const { return id; }
std::vector<item> const& get_items() const { return items; }
void add_item(item::type t, std::string const& n)
{ items.emplace_back(t, n); }
private:
int id;
std::vector<item> items;
};
我将要概述的解决方案的核心是以下类(class) order_repository ,及其内部用法 std::function保存客户端注册的回调。register_callback() 注册函数,并且(非常直观地)通过 unregister_callback() 取消注册通过提供 registered_callback() 返回的 cookie 来实现功能注册时:place_order()用于下订单的功能,以及 process_order()触发所有订单处理的函数。这将导致所有注册的处理程序被顺序调用。每个处理程序接收对相同下单 vector 的引用:#include <functional>
using order_ptr = std::shared_ptr<order>; // Just a useful type alias
class order_repository // Collects orders and registers processing callbacks
{
public:
typedef std::function<void(std::vector<order_ptr>&)> order_callback;
template<typename F>
size_t register_callback(F&& f)
{ return callbacks.push_back(std::forward<F>(f)); }
void place_order(order_ptr o)
{ orders.push_back(o); }
void process_all_orders()
{ for (auto const& cb : callbacks) { cb(orders); } }
private:
std::vector<order_callback> callbacks;
std::vector<order_ptr> orders;
};
此解决方案的优势来自于 std::function 的使用。实现类型删除和allow encapsulating any kind of callable object .void generate_and_place_orders(order_repository& r)
{
order_ptr o = std::make_shared<order>(42);
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
o = std::make_shared<order>(1729);
o->add_item(item::book, "TC++PL, 4th Edition");
o->add_item(item::book, "C++ Concurrency in Action");
r.place_order(o);
o = std::make_shared<order>(24);
o->add_item(item::dvd, "2001: A Space Odyssey");
r.place_order(o);
o = std::make_shared<order>(9271);
o->add_item(item::dvd, "The Big Lebowski");
o->add_item(item::book, "C++ Concurrency in Action");
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
}
void print_all_orders(std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id() << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
std::cout << "=========================\n\n";
}
以及一个使用它的简单程序:int main()
{
order_repository r;
generate_and_place_orders(r);
// Register a regular function as a callback...
r.register_callback(print_all_orders);
// Process the order! (Will invoke all the registered callbacks)
r.process_all_orders();
}
这是live example显示该程序的输出。print_all_orders() 相同的详细订单列表。以上,或不包括订单项目的简短摘要:struct print_all_orders
{
print_all_orders(bool detailed) : printDetails(detailed) { }
void operator () (std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id();
if (printDetails)
{
std::cout << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
else { std::cout << std::endl; }
}
std::cout << "=========================\n\n";
}
private:
bool printDetails;
};
以下是如何在小型测试程序中使用它:int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
// Register one particular instance of our functor...
r.register_callback(print_all_orders(false));
// Register another instance of the same functor...
r.register_callback(print_all_orders(true));
r.process_all_orders();
}
这是 this live example 中显示的相应输出.std::function 提供的灵活性,我们也可以注册std::bind()的结果作为回调。为了用一个例子来证明这一点,让我们引入另一个类 person :#include <iostream>
struct person
{
person(std::string n) : name(n) { }
void receive_order(order_ptr spOrder)
{ std::cout << name << " received order " << spOrder->get_id() << std::endl; }
private:
std::string name;
};
类(class)person有一个成员函数 receive_order() .调用 receive_order()某某person对象模拟特定 order 的事实已经送到了person .void give_all_orders_to(std::vector<order_ptr>& orders, person& p)
{
std::cout << "Dispatching orders:\n=========================\n";
for (auto const& o : orders) { p.receive_order(o); }
orders.clear();
std::cout << "=========================\n\n";
}
此时我们可以编写如下程序,注册两个回调函数:与我们之前使用的打印订单相同的函数,以及将订单调度到Person的某个实例的上述函数。 .这是我们如何做到的:int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
person alice("alice");
r.register_callback(print_all_orders);
// Register the result of binding a function's argument...
r.register_callback(std::bind(give_all_orders_to, _1, std::ref(alice)));
r.process_all_orders();
}
该程序的输出显示在 this live example 中.int main()
{
order_repository r;
generate_and_place_orders(r);
person alice("alice");
person bob("bob");
r.register_callback(print_all_orders);
r.register_callback([&] (std::vector<order_ptr>& orders)
{
for (auto const& o : orders)
{
if (o->get_items().size() < 2) { bob.receive_order(o); }
else { alice.receive_order(o); }
}
orders.clear();
});
r.process_all_orders();
}
再次,this live example显示相应的输出。struct Hello
{
void operator()() const
{
std::cout << "Hello";
}
};
struct World
{
void operator()() const
{
std::cout << ", World!" << std::endl;
}
};
int main()
{
boost::signals2::signal<void ()> sig;
sig.connect(Hello());
sig.connect(World());
sig();
}
像往常一样,这里是 live example对于上述程序。
关于c++ - 有没有一种惯用的方法来在 C++ 中创建一组委托(delegate)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15483505/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2