dlib/examples/pipe_ex_2.cpp

156 lines
4.5 KiB
C++
Raw Normal View History

// 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.
*/
// And here we have a typedef for the pipe we will be using
typedef dlib::pipe<tsu_type>::kernel_1a pipe_type;
// ----------------------------------------------------------------------------------------
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.
pipe_type message_pipe;
// 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;
}
private:
void thread ()
{
tsu_type msg;
// Here we loop on messages from the message_pipe.
while (message_pipe.dequeue(msg))
{
// Tell the msg type_safe_union object to take whatever object
// 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);
}
}
};
// ----------------------------------------------------------------------------------------
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.
}
// ----------------------------------------------------------------------------------------