跳到主要内容

Boost.Signals2

阅读量

0

阅读人次

0

介绍

Boost.Signals2库是一个管理信号和槽函数系统的实现。信号代表具有多个目标的回调,在相似系统中也称为发布者或事件。信号连接到一组槽函数,这些槽函数是回调接收器(也称为事件目标或订阅者),在“发出”信号时调用它们。

管理信号和槽函数,因为信号和槽函数(或更恰当地说,是作为槽函数的一部分出现的对象)可以跟踪连接,并且能够在其中一个被销毁时自动断开信号/插槽的连接。 这使用户可以进行信号/槽函数连接,而无需花费很大的精力来管理所有相关对象的生命周期。

当信号连接到多个槽函数时,存在关于槽函数的返回值与信号的返回值之间关系的问题。 Boost.Signals2允许用户指定组合多个返回值的方式。

Signals2

本文档描述了原始Boost.Signals库的线程安全变体。 为了支持线程安全,对接口进行了一些更改,主要是在自动连接管理方面。 此实现由Frank Mori Hess编写。 也要感谢Timmo Stange,Peter Dimov和Tony Van Eerd的想法和反馈,也要感谢Douglas Gregor的Boost的原始版本。

教程

如何阅读本教程

本教程不适合线性阅读。 它的顶层结构将库中的不同概念大致分开(例如,处理调用多个槽函数,将值传递到槽函数和从槽函数传递值),在这些概念中,首先介绍基本概念,然后在以后描述该库的更复杂的用法 。 每个部分均标记为BeginnerIntermediateAdvanced,以帮助指导读者。 初学者部分包括所有库用户都应该知道的信息; 在只阅读了Beginner部分之后,您可以充分利用Signals2库。 中级部分建立在初学者部分的基础上,该库的使用稍微复杂一些。 最后,“高级”部分详细介绍了Signals2库的非常高级的用法,这些用法通常需要扎实的入门和中级主题知识; 大多数用户不需要阅读“高级”部分。

Hello,World!(Beginner)

下面的例子使用信号和槽函数来输出"Hello,World!"。首先,我们创建一个信号sig,一个不带参数的信号,并且有一个void返回值。接下来,我们使用connect方法将hello函数连接到信号。

最后,使用像函数一样的信号sig来调用槽,它依次调用`HelloWorld::operator()来打印"Hello,World!"。

struct HelloWorld {
void operator()() const {
std::cout << "Hello, World!" << std::endl;
}
};
// Signal with no arguments and a void return value
boost::signals2::signal<void ()> sig;

// Connect a HelloWorld slot
HelloWorld hello;
sig.connect(hello);

// Call all of the slots
sig();

调用多个槽函数

连接多个槽函数(Beginner)

从一个信号中调用一个单独的槽函数并不是很有趣,所以我们可以通过将打印“Hello,World!”的工作分成两个完全独立的槽函数来让Hello,World程序变得更有趣。第一个槽函数会打印“Hello”,可能是这样的:

struct Hello {
void operator()() const {
std::cout << "Hello";
}
};

第二个槽函数将打印“,World!”和一个换行符,来完成这个程序。第二个槽函数可能是这样的:

struct World {
void operator()() const {
std::cout << ", World!" << std::endl;
}
};

就像在前面的例子中一样,我们可以创建一个没有参数的信号sig,并且有一个void返回值。这一次,我们将hello和world槽函数连接到相同的信号,当我们调用信号时,两个槽都将被调用。

boost::signals2::signal<void ()> sig;

# 这里Hello()后面带括号是构造了临时对象,不是调用operator()
sig.connect(Hello());
sig.connect(World());

sig();

默认情况下,槽函数被push到槽函数list的后面,因此这个程序的输出将如预期的那样:

Hello, World!

顺序调用槽函数(Itermediate)

槽函数可以自由地产生副作用,这意味着有些槽函数必须在其他槽函数之前被调用,即使它们没有按顺序连接。Boost.Signals2库允许将插槽放置在以某种方式排序的组中。对于我们的Hello,World程序,我们想要“Hello”在“,World!”之前打印出来。因此,我们将“Hello”放入一个必须在“,World!”的组之前执行的组中。为了做到这一点,我们可以在指定该组的connect调用的开头提供一个额外的参数。group值默认为int,并且由从小到大排序。下面是我们如何构建Hello,World:

boost::signals2::signal<void ()> sig;
sig.connect(1, World()); // connect with group 1
sig.connect(0, Hello()); // connect with group 0

调用该信号将正确地打印“Hello,World!”因为Hello对象在组0中,在第1组中Word对象所在的位置前面。实际上,group参数是可选的。我们在第一个Hello,World例子中省略了它,因为当所有的槽都是独立的时候没有必要。那么,如果我们把调用连接起来使用组参数和那些没有的调用会发生什么呢?“unnamed”的槽函数(即那些在没有指定group的情况下被连接的人可以被放置在槽函数list的前面或后面(通过boost::signals2::at_frontboost::signals2::at_back作为最后一个连接的参数),并且默认为list的末尾。当一个组被指定时,最后的at_frontat_back参数描述了在组排序中放置位置的位置。与at_front连接的未分组槽函数将始终位于所有分组插槽之前。 与at_back连接的未分组槽函数将始终在所有分组槽函数之后。

如果我们在这个例子中添加一个新的槽函数:

struct GoodMorning {
void operator()() const {
std::cout << "... and good morning!" << std::endl;
}
};
// by default slots are connected at the end of the slot list
sig.connect(GoodMorning());

// slots are invoked this order:
// 1) ungrouped slots connected with boost::signals2::at_front
// 2) grouped slots according to ordering of their groups
// 3) ungrouped slots connected with boost::signals2::at_back
sig();

…我们会得到我们想要的结果:

Hello, World!
... and good morning!

向槽函数传递值

槽函数的参数(Beginner)

信号可以将参数传递到他们调用的每个槽函数。例如,传递鼠标移动事件的信号可能想要传递新的鼠标坐标,以及鼠标按钮是否被按下。

例如,我们将创建一个将两个float参数传递到其槽函数的信号。 然后,我们将创建一些插槽,在这些值上打印各种算术运算的结果。

void print_args(float x, float y) {
std::cout << "The arguments are " << x << " and " << y << std::endl;
}

void print_sum(float x, float y) {
std::cout << "The sum is " << x + y << std::endl;
}

void print_product(float x, float y) {
std::cout << "The product is " << x * y << std::endl;
}

void print_difference(float x, float y) {
std::cout << "The difference is " << x - y << std::endl;
}

void print_quotient(float x, float y) {
std::cout << "The quotient is " << x / y << std::endl;
}
boost::signals2::signal<void (float, float)> sig;

sig.connect(&print_args);
sig.connect(&print_sum);
sig.connect(&print_product);
sig.connect(&print_difference);
sig.connect(&print_quotient);

sig(5., 3.);

这个程序将打印出以下内容:

The arguments are 5 and 3
The sum is 8
The product is 15
The difference is 2
The quotient is 1.66667

因此,像函数一样被调用时给sig的任何值都将传递到每个槽函数。 创建信号时,我们必须预先声明这些值的类型。 类型boost::signals2::signal <void(float,float)>表示信号具有void返回值,并传入两个float值。 因此,连接到sig的任何插槽都必须能够传入两个float值。

信号返回值(Advanced)

正如槽函数可以接收参数一样,它们也可以返回值。 然后可以通过combiner将这些值返回给信号的调用者。 combiner是一种机制,可以获取调用槽函数的结果(可能没有结果或一百个结果;直到程序运行时我们才知道),并将它们合并为一个结果以返回给调用者。 单个结果通常是槽函数调用结果的简单函数:最近一次槽函数调用的结果,任何槽函数返回的最大值或所有结果的容器都是有可能的。

我们可以略微修改前面的算术运算示例,以便所有槽函数都返回计算乘积,商,和或差的结果。 然后,信号本身可以根据这些要打印的结果返回一个值:

float product(float x, float y) { return x * y; }
float quotient(float x, float y) { return x / y; }
float sum(float x, float y) { return x + y; }
float difference(float x, float y) { return x - y; }
boost::signals2::signal<float (float, float)> sig;
sig.connect(&product);
sig.connect(&quotient);
sig.connect(&sum);
sig.connect(&difference);

// The default combiner returns a boost::optional containing the return
// value of the last slot in the slot list, in this case the
// difference function.
std::cout << *sig(5, 3) << std::endl;

该示例程序将输出2。这是因为具有返回类型(float,给boost::signals2::signal类模板的第一个模板参数)的信号的默认行为是调用所有槽函数,然后返回boost::optional包含最后一个调用的槽函数返回的结果。 对于此示例,此行为无疑是愚蠢的,因为槽函数没有副作用,并且结果是连接的最后一个槽函数。

一个更有趣的信号结果将是任何槽函数返回的最大值。 为此,我们创建一个自定义combiner ,如下所示:

// combiner which returns the maximum value returned by all slots
template<typename T>
struct maximum {
typedef T result_type;

template<typename InputIterator>
T operator()(InputIterator first, InputIterator last) const {
// If there are no slots to call, just return the
// default-constructed value
if(first == last ) return T();
T max_value = *first++;
while (first != last) {
if (max_value < *first) max_value = *first;
++first;
}
return max_value;
}
};

maximum类模板充当函数对象。 其结果类型由其模板参数指定,这是它希望根据其计算最大值的类型(例如,maximum <float>将在一系列浮点数中找到最大浮点数)。 调用maximum对象时,将为其提供输入迭代器序列[first,last),其中包括调用所有槽函数的结果。 maximum使用此输入迭代器序列来计算最大值元素,并返回该最大值。

实际上,我们通过将其安装为信号的combiner 来使用此新功能对象类型。combiner 模板参数遵循信号的调用签名:

boost::signals2::signal<float (float x, float y),maximum<float> > sig;

现在我们可以连接执行算术函数和使用信号的槽函数:

sig.connect(&product);
sig.connect(&quotient);
sig.connect(&sum);
sig.connect(&difference);

// Outputs the maximum value returned by the connected slots, in this case
// 15 from the product function.
std::cout << "maximum: " << sig(5, 3) << std::endl;

该程序的输出将为15,因为不管槽函数的连接顺序如何,5和3的乘积都将大于商,和或差。

在其他情况下,我们可能希望在一个大型数据结构中一起返回所有由槽函数计算的值。 使用不同的combiner可以轻松完成此操作:

// aggregate_values is a combiner which places all the values returned
// from slots into a container
template<typename Container>
struct aggregate_values {
typedef Container result_type;

template<typename InputIterator>
Container operator()(InputIterator first, InputIterator last) const {
Container values;
while(first != last) {
values.push_back(*first);
++first;
}
return values;
}
};

同样,我们可以用这个新combiner来创建一个signal

boost::signals2::signal<float(float, float),aggregate_values<std::vector<float>>> sig;
sig.connect(&quotient);
sig.connect(&product);
sig.connect(&sum);
sig.connect(&difference);

std::vector<float> results = sig(5, 3);
std::cout << "aggregate values: ";
std::copy(results.begin(), results.end(),
std::ostream_iterator<float>(std::cout, " "));
std::cout << "\n";

该程序的输出将包含15、8、1.6667和2。这里有趣的是,signal类的第一个模板参数float实际上不是信号的返回类型。 相反,它是连接的槽函数使用的返回类型,并且还将是传递到combiner的输入迭代器的value_type。 combiner本身是一个函数对象,其result_type成员类型成为signal的返回类型。

传递到combiner的输入迭代器将解引用操作(例如上例的*first)转换为槽函数调用。 因此,combiner可以选择仅调用某些槽函数,直到满足某些特定条件为止。 例如,在分布式计算系统中,combiner可以询问每个远程系统是否将处理该请求。 只有一个远程系统需要处理特定的请求,因此在一个远程系统接受工作之后,我们不想让任何其他远程系统执行相同的任务。 这样的组合器仅需要在解引用迭代器时检查返回的值,并在该值可接受时返回。 下面的组合器将第一个非nullptr指针返回到FulfilledRequest数据结构,而不会要求任何以后的槽函数来满足请求:

struct DistributeRequest {
typedef FulfilledRequest* result_type;

template<typename InputIterator>
result_type operator()(InputIterator first, InputIterator last) const {
while (first != last) {
if (result_type fulfilled = *first) return fulfilled;
++first;
}
return 0;
}
};

连接管理

断开槽函数(Beginner)

槽函数连接后不会无限期地存在。 通常,槽函数仅用于接收一些事件,然后断开连接,程序员需要控制权来决定何时不再连接槽函数。

显式管理连接的入口点是boost::signals2::connection类。connection类唯一表示特定信号和特定槽函数之间的连接。 connected()方法检查信号和槽函数是否仍然连接。如果信号和槽函数在调用disconnect()之前已连接,则disconnect()会断开它们的连接。 每次调用signalconnect()方法都会返回一个connection对象,该对象可用于确定连接是否仍然存在或断开信号和槽函数的连接。

boost::signals2::connection c = sig.connect(HelloWorld());
std::cout << "c is connected\n";
sig(); // Prints "Hello, World!"

c.disconnect(); // Disconnect the HelloWorld object
std::cout << "c is disconnected\n";
sig(); // Does nothing: there are no connected slots

闭塞槽函数(Beginner)

槽函数可以暂时“闭塞”,这意味着在调用信号但尚未永久断开连接时将忽略它们,不进行调用。 通常,这用于防止无限递归,否则在其他情况下运行槽函数会导致再次调用与其连接的信号。 一个boost::signals2::shared_connection_block对象将临时闭塞一个槽函数。 通过销毁或调用引用该连接的所有shared_connection_block对象上的unblock来解除连接的阻塞。 这是一个阻止/取消阻止插槽的示例:

boost::signals2::connection c = sig.connect(HelloWorld());
std::cout << "c is not blocked.\n";
sig(); // Prints "Hello, World!"

{
boost::signals2::shared_connection_block block(c); // block the slot
std::cout << "c is blocked.\n";
sig(); // No output: the slot is blocked
} // shared_connection_block going out of scope unblocks the slot
std::cout << "c is not blocked.\n";
sig(); // Prints "Hello, World!"}

作用域连接(Intermediate)

boost::signals2::scoped_connection类引用一个信号/槽函数的connection ,当scoped_connection类超出范围时,它将断开连接。 当连接仅需要临时连接时,例如,

{
boost::signals2::scoped_connection c(sig.connect(ShortLived()));
sig(); // will call ShortLived function object
} // scoped_connection goes out of scope and disconnects

sig(); // ShortLived function object no longer connected to sig

注意,尝试使用赋值语法初始化scoped_connection会失败,因为它是不可复制的。 显式初始化语法或默认构造,然后从signals2 :: connection进行赋值将起作用:

// doesn't compile due to compiler attempting to copy a 
// temporary scoped_connection object
// boost::signals2::scoped_connection c0 = sig.connect(ShortLived());

// okay
boost::signals2::scoped_connection c1(sig.connect(ShortLived()));

// also okay
boost::signals2::scoped_connection c2;
c2 = sig.connect(ShortLived());

断开等效槽函数(中间)

只要函数对象的类型具有可访问的==运算符,就可以使用 signal::disconnect方法的形式断开与给定函数对象相等的槽函数。 例如:

void foo() { std::cout << "foo"; }
void bar() { std::cout << "bar\n"; }
boost::signals2::signal<void ()> sig;
sig.connect(&foo);
sig.connect(&bar);
sig();

// disconnects foo, but not bar
sig.disconnect(&foo);
sig();

自动连接管理(Intermediate)

Boost.Signals2可以自动跟踪信号/槽函数连接中涉及的对象的生命周期,包括在槽函数调用中涉及的对象被销毁时自动断开槽函数。 例如,考虑一个简单的新闻传递服务,其中客户端连接到新闻提供者,然后在信息到达时将新闻发送给所有连接的客户端。 新闻传递服务可以这样构造:

class NewsItem { /* ... */ };

typedef boost::signals2::signal<void (const NewsItem&)> signal_type;
signal_type deliverNews;

希望接收新闻更新的客户端只需要将可以接收新闻items的函数对象连接到deliveryNews信号即可。 例如,我们的应用程序中可能有一个专门用于新闻的特殊消息区域,例如:

struct NewsMessageArea : public MessageArea {
public:
// ...
void displayNews(const NewsItem& news) const {
messageText = news.text();
update();
}
};

// ...
NewsMessageArea *newsMessageArea = new NewsMessageArea(/* ... */);
// ...
deliverNews.connect(boost::bind(&NewsMessageArea::displayNews,newsMessageArea, _1));

但是,如果用户关闭NewsMessageArea,破坏了deliveryNews知道的newsMessageArea对象,该怎么办? 最有可能发生段错误。 但是,使用Boost.Signals2,可以使用slot::track跟踪由shared_ptr管理的任何对象。 当槽函数的任何跟踪对象过期时,槽函数将自动断开连接。 另外,Boost.Signals2将确保与之关联的槽函数在执行过程中没有跟踪对象过期。 它是通过在执行槽函数之前创建槽函数的跟踪对象的临时shared_ptr副本来实现的。 要跟踪NewsMessageArea,我们使用shared_ptr来管理其生命周期,并在连接它之前通过它的slot::track方法将shared_ptr传递到该槽函数,例如:

// ...
boost::shared_ptr<NewsMessageArea> newsMessageArea(new NewsMessageArea(/* ... */));
// ...
deliverNews.connect(signal_type::slot_type(&NewsMessageArea::displayNews,
newsMessageArea.get(), _1).track(newsMessageArea));

注意,在上面的示例中不需要显式调用bind()。 如果signals2::slot构造函数传递了多个参数,它将自动传递所有参数以绑定并使用返回的函数对象。

还要注意,我们使用newsMessageArea.get()而不是shared_ptr本身将普通指针作为第二个参数传递给槽函数构造函数。 如果我们已传递了newsMessageArea本身,则将已将shared_ptr的副本绑定到slot函数中,以防止shared_ptr过期。 但是,使用slot::track表示我们希望允许跟踪的对象过期,并在发生这种情况时自动断开连接。

boost::shared_ptr以外的shared_ptr类(例如std :: shared_ptr)也可能出于连接管理目的而被跟踪。 slot :: track_foreign方法支持它们。

后构造函数和前析构函数 (Advanced)

使用shared_ptr进行跟踪的一个限制是对象无法在其构造函数中设置对其自身的跟踪。但是,可以在创建对象并将其传递给shared_ptr之后调用的后构造函数中设置跟踪。 Boost.Signals2库通过deconstruct()工厂函数为后构造器和预构造器提供支持。

在大多数情况下,为类设置后构造函数的最简单,最可靠的方法是定义一个关联的adl_postconstruct函数,该函数可通过deconstruct()找到,将类的构造函数设为私有,并通过声明deconstruct_access来提供对私有构造函数的解构访问一个友元类。这将确保只能通过deconstruct()函数创建该类的对象,并且将始终调用其关联的adl_postconstruct()函数。

示例部分包含几个示例,这些示例使用后构造器和预构造器定义类,并使用deconstruct()创建这些类的对象。

请注意,Boost.Signals2中对postconstructor/predestructor的支持对于使用该库绝对不是必不可少的。使用deconstruct完全是可选的。一种替代方法是为您的类定义静态工厂函数。工厂函数可以创建一个对象,将该对象的所有权传递给shared_ptr,设置对对象的跟踪,然后返回shared_ptr

什么时候会发生断开?(Intermediate)

当这些情况发生时,信号/槽函数断开:

  • 通过connectiondisconnect方法直接或间接地通过signaldisconnect方法或scoped_connection的析构函数显式地断开连接。
  • 被槽跟踪的对象被销毁。
  • signal被销毁了。

这些事件可以随时发生,而不会中断signal的调用顺序。如果在信号的调用序列中的任何时间断开信号/槽函数连接,则调用序列仍将继续,但不会调用已断开的槽函数。此外,信号处于调用序列中时可能会被破坏,在这种情况下,它将完成其时隙调用序列,但可能无法直接访问。

可以递归地调用信号(例如,信号A调用槽函数B,槽函数B调用信号A ...)。在递归情况下,断开行为不会改变,除了槽函数当前正在调用执行的序列包括针对信号的所有嵌套调用的槽函数调用。

请注意,即使在断开连接之后,其关联的槽函数也可能仍在执行过程中。换句话说,断开连接不会阻止等待连接的关联槽函数完成执行。如果断开与信号调用同时发生,则此情况可能在多线程环境中发生,或者如果插槽自身断开,则可能在单线程环境中发生这种情况。

传递槽函数(Intermediate)

Boost.Signals2库的槽函数是由任意函数对象创建的,因此没有固定的类型。然而,通常要求槽函数通过不能作为模板的接口传递。槽可以通过slot_type为每个特定的信号类型传递,任何与信号签名兼容的函数对象都可以传递给slot_type参数。例如:

// a pretend GUI button
class Button {
typedef boost::signals2::signal<void (int x, int y)> OnClick;
public:
typedef OnClick::slot_type OnClickSlotType;

// forward slots through Button interface to its private signal
boost::signals2::connection doOnClick(const OnClickSlotType & slot);

// simulate user clicking on GUI button at coordinates 52, 38
void simulateClick();
private:
OnClick onClick;
};

boost::signals2::connection Button::doOnClick(const OnClickSlotType & slot) {
return onClick.connect(slot);
}

void Button::simulateClick() {
onClick(52, 38);
}

void printCoordinates(long x, long y) {
std::cout << "(" << x << ", " << y << ")\n";
}
Button button;
button.doOnClick(&printCoordinates);
button.simulateClick();

doOnClick方法现在在功能上等同于onClick信号的connect方法,但是doOnClick方法的详细信息可以隐藏在实现文件中。

示例:文档视图

信号可用于实现灵活的Document-View体系结构。 该Document将包含每个View可以连接的signal。 以下Document类定义了一个支持多种视图的简单文本文档。 请注意,它存储一个信号,所有视图都将连接到该信号。

class Document {
public:
typedef boost::signals2::signal<void ()> signal_t;

Document(){}

/* Connect a slot to the signal which will be emitted whenever
text is appended to the document. */
boost::signals2::connection connect(const signal_t::slot_type &subscriber) {
return m_sig.connect(subscriber);
}

void append(const char* s) {
m_text += s;
m_sig();
}

const std::string& getText() const {
return m_text;
}

private:
signal_t m_sig;
std::string m_text;
};

接下来,我们可以开始定义View。下面的TextView类提供了文档文本的简单视图。

class TextView {
public:
TextView(Document& doc): m_document(doc) {
m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
}

~TextView() {
m_connection.disconnect();
}

void refresh() const {
std::cout << "TextView: " << m_document.getText() << std::endl;
}
private:
Document& m_document;
boost::signals2::connection m_connection;
};

同时,我们可以使用HexView视图提供转换为十六进制值的文档视图:

class HexView {
public:
HexView(Document& doc): m_document(doc) {
m_connection = m_document.connect(boost::bind(&HexView::refresh, this));
}

~HexView() {
m_connection.disconnect();
}

void refresh() const {
const std::string& s = m_document.getText();
std::cout << "HexView:";
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it)
std::cout << ' ' << std::hex << static_cast<int>(*it);
std::cout << std::endl;
}
private:
Document& m_document;
boost::signals2::connection m_connection;
};

为了将示例联系在一起,这里有一个简单的主函数,它设置两个视图,然后修改文档:

int main(int argc, char* argv[]) {
Document doc;
TextView v1(doc);
HexView v2(doc);

doc.append(argc == 2 ? argv[1] : "Hello world!");
return 0;
}

完整的示例源代码由Keith MacDonald提供,可以在示例部分找到。我们还提供了程序的变体,该程序使用自动连接管理来断开连接当视图销毁时。

提供槽函数对其连接的访问权限(Advanced)

您可能会遇到希望从槽函数本身内部断开或阻止槽函数连接的情况。 例如,假设您有一组异步任务,每个异步任务完成时都会发出信号。 您希望将槽函数连接到所有任务以在完成每个任务时检索其结果。 给定任务完成并运行槽函数后,就不再需要将槽函数连接到已完成的任务。 因此,您可能希望通过使槽函数在运行时断开其调用连接来清理旧的连接。

为了使槽函数断开(或阻止)其调用连接,它必须有权访问signals2::connection对象,该对象引用了调用信号槽函数的连接。 困难在于,连接对象是由signal::connect方法返回的,因此直到将槽函数连接到信号后才可用。 这在多线程环境中尤其麻烦,在多线程环境中,当连接槽函数时,信号可能会被另一个线程同时调用。

因此,signal类提供signal::connect_extended方法,该方法允许将带有额外参数的槽函数连接到信号。 额外的参数是signals2::connection对象,该对象引用当前正在调用槽函数的信号函数的connectionsignal::connect_extended使用 typedef给定类型signal :: extended_slot_type的槽函数。

示例部分包括一个extended_slot程序,该程序演示了使用signal :: connect_extended的语法。

更改Signal的互斥类型(Advanced)

在大多数情况下,signals2::signalMutex模板类型参数的boost::signals2::mutex的默认类型应该很好。 如果希望使用备用互斥类型,则该互斥类型必须是默认可构造的,并且必须满足Boost.Thread库定义的Lockable概念。 也就是说,它必须具有lock()unlock()方法(Lockable概念还包括try_lock()方法,但该库不需要try_lock())。

Boost.Signals2库提供了一种用于signal的备用互斥体类:boost::signals2::dummy_mutex。 这是伪造的互斥锁,用于单线程程序,锁定一个真正的互斥锁将是无用的开销。 可以与信号一起使用的其他互斥类型包括boost::mutex或C ++ 11中的std::mutex

更改信号的互斥对象模板类型参数可能很繁琐,因为它前面有大量模板参数。 signal_type元函数在这种情况下特别有用,因为它启用了signal2::signal类的命名模板类型参数。 例如,要声明一个以int作为参数并使用boost :: signals2 :: dummy_mutex作为其Mutex类型的信号,您可以编写:

namespace bs2 = boost::signals2;
using namespace bs2::keywords;
bs2::signal_type<void (int), mutex_type<bs2::dummy_mutex> >::type sig;

链接到Signals2库

与原始的Boost.Signals库不同,Boost.Signals2当前仅是头文件。

范例程序

其他教程示例

文档视图

使用deconstruct()的后构造函数和预析构函数