pipe_ex_2.cpp 4.58 KB
Newer Older
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


/*
    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 
Davis King's avatar
Davis King committed
75
        // message_pipe.dequeue() to return false so our thread will terminate
76
77
        message_pipe.disable();

Davis King's avatar
Davis King committed
78
        // now block until our thread has terminated
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
        wait();
    }

    // Here we declare our pipe object.  It will contain our messages.
    // There are only two requirements on the type of objects you can use in a
    // pipe, first they must have a default constructor and second they must
    // be swappable by a global swap().
    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
Davis King's avatar
Davis King committed
135
    msg.get<float>() = 4.567f;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    // 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.
}

// ----------------------------------------------------------------------------------------