1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*
This is an example showing how to use the type_safe_union and pipe object from
from the dlib C++ Library to send messages between threads.
In this example we will create a class with a single thread in it. This thread
will receive messages from a pipe object and simply print them to the screen.
The interesting thing about this example is that it shows how to use a pipe and
type_safe_union to create a message channel between threads that can send many
different types of objects in a type safe manner.
Program output:
got a float: 4.567
got a string: string message
got an int: 7
got a string: yet another string message
*/
#include <dlib/threads.h>
#include <dlib/pipe.h>
#include <dlib/type_safe_union.h>
#include <iostream>
using namespace dlib;
using namespace std;
// ----------------------------------------------------------------------------------------
typedef type_safe_union<int, float, std::string> tsu_type;
/* This is a typedef for the type_safe_union we will be using in this example.
This type_safe_union object is a type-safe analogue of a union declared as follows:
union our_union_type
{
int a;
float b;
std::string c;
};
Note that the above union isn't actually valid C++ code because it contains a
non-POD type. That is, you can't put a std::string or any non-trivial
C++ class in a union. The type_safe_union, however, enables you to store non-POD
types such as the std::string.
*/
// ----------------------------------------------------------------------------------------
class pipe_example : private threaded_object
{
public:
pipe_example(
) :
message_pipe(4) // This 4 here is the size of our message_pipe. The significance is that
// if you try to enqueue more than 4 messages onto the pipe then enqueue() will
// block until there is room.
{
// start the thread
start();
}
~pipe_example (
)
{
// wait for all the messages to be processed
message_pipe.wait_until_empty();
// Now disable the message_pipe. Doing this will cause all calls to
// message_pipe.dequeue() to return false so our thread will terminate
message_pipe.disable();
// now block until our thread has terminated
wait();
}
// Here we declare our pipe object. It will contain our messages.
dlib::pipe<tsu_type> message_pipe;
private:
// When we call apply_to_contents() below these are the
// functions which get called.
void operator() (int val)
{
cout << "got an int: " << val << endl;
}
void operator() (float val)
{
cout << "got a float: " << val << endl;
}
void operator() (std::string val)
{
cout << "got a string: " << val << endl;
}
void thread ()
{
tsu_type msg;
// Here we loop on messages from the message_pipe.
while (message_pipe.dequeue(msg))
{
// Here we call the apply_to_contents() function on our type_safe_union.
// It takes a function object and applies that function object
// to the contents of the union. In our case we have setup
// the pipe_example class as our function object and so below we
// tell the msg object to take whatever it contains and
// call (*this)(contained_object); So what happens here is
// one of the three above functions gets called with the message
// we just got.
msg.apply_to_contents(*this);
}
}
// Finally, note that since we declared the operator() member functions
// private we need to declare the type_safe_union as a friend of this
// class so that it will be able to call them.
friend class type_safe_union<int, float, std::string>;
};
// ----------------------------------------------------------------------------------------
int main()
{
pipe_example pe;
// Make one of our type_safe_union objects
tsu_type msg;
// Treat our msg as a float and assign it 4.567
msg.get<float>() = 4.567f;
// Now put the message into the pipe
pe.message_pipe.enqueue(msg);
// Put a string into the pipe
msg.get<std::string>() = "string message";
pe.message_pipe.enqueue(msg);
// And now an int
msg.get<int>() = 7;
pe.message_pipe.enqueue(msg);
// And another string
msg.get<std::string>() = "yet another string message";
pe.message_pipe.enqueue(msg);
// the main function won't really terminate here. It will call the destructor for pe
// which will block until all the messages have been processed.
}
// ----------------------------------------------------------------------------------------
|