NMEA: changed signature of nmea::make_sentence to make it more robust.
[marnav.git] / examples / select.cpp
blob267b289cf732aac7af4abf355f80cadf104f9545
1 #include <memory>
2 #include <vector>
3 #include <iostream>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include <marnav/io/device.hpp>
9 #include <marnav/io/selectable.hpp>
11 namespace marnav_example
13 /// A named pipe, implementing device and selectable for demonstration purposes.
14 ///
15 /// @note The created FIFO will not be removed.
16 class pipe : public marnav::io::device, virtual public marnav::io::selectable
18 friend class selector;
20 public:
21 pipe(std::string filename)
22 : fd(-1)
23 , filename(filename)
27 virtual ~pipe() { close(); }
29 void create() { ::mkfifo(filename.c_str(), 0666); }
31 void open_read()
33 if (fd >= 0)
34 return;
35 fd = ::open(filename.c_str(), O_RDONLY | O_NONBLOCK);
38 void open_write()
40 if (fd >= 0)
41 return;
42 fd = ::open(filename.c_str(), O_WRONLY | O_NONBLOCK);
45 void open_read_write()
47 if (fd >= 0)
48 return;
49 fd = ::open(filename.c_str(), O_RDWR | O_NONBLOCK);
52 virtual void close() override
54 if (fd < 0)
55 return;
56 ::close(fd);
57 fd = -1;
60 virtual int read(char * buffer, uint32_t size) override
62 if (!buffer)
63 throw std::invalid_argument{"buffer"};
64 if (fd < 0)
65 throw std::runtime_error{"pipe not open"};
67 return ::read(fd, buffer, size);
70 virtual int write(const char * buffer, uint32_t size) override
72 if (!buffer)
73 throw std::invalid_argument{"buffer"};
74 if (fd < 0)
75 throw std::runtime_error{"pipe not open"};
77 return ::write(fd, buffer, size);
80 protected:
81 virtual void open() override {}
82 virtual int get_fd() const override { return fd; }
84 private:
85 int fd;
86 std::string filename;
89 class selector
91 public:
92 std::vector<std::shared_ptr<marnav::io::selectable>> operator()(
93 const std::vector<std::shared_ptr<marnav::io::selectable>> & devices)
95 return select(devices);
98 static std::vector<std::shared_ptr<marnav::io::selectable>> select(
99 const std::vector<std::shared_ptr<marnav::io::selectable>> & devices)
101 std::vector<std::shared_ptr<marnav::io::selectable>> result;
103 if (devices.size() == 0)
104 return result;
106 // fill filedescriptor set for reading
107 fd_set rfds;
108 FD_ZERO(&rfds);
109 int max_fd = -1;
110 for (auto const & dev : devices) {
111 auto fd = dev->get_fd();
112 FD_SET(fd, &rfds);
113 if (fd > max_fd)
114 max_fd = fd;
117 // the actual select
118 auto rc = ::select(max_fd + 1, &rfds, 0, 0, 0);
119 if (rc < 0)
120 throw std::runtime_error{"select"};
122 // find out which device has caused the wakeup
123 result.reserve(rc);
124 for (auto const & dev : devices) {
125 if (FD_ISSET(dev->get_fd(), &rfds)) {
126 result.push_back(dev);
129 return result;
134 int main(int, char **)
136 // create a named pipe
137 auto dev0 = std::make_shared<marnav_example::pipe>("/tmp/foobar");
138 dev0->create();
139 dev0->open_read();
141 // open it a second time as different device
142 auto dev1 = std::make_shared<marnav_example::pipe>("/tmp/foobar");
143 dev1->open_read_write();
145 // write some data
146 dev1->write("foo\0", 4);
148 // wait on named pipe, receive and print data
149 auto devs = marnav_example::selector::select({dev0});
150 if (devs.size() == 0) {
151 std::cout << "no devices ready for read, probably timeout\n";
152 return -1;
155 for (auto & dev : devs) {
156 char buffer[128];
157 int rc = -1;
159 if (dev0 == dev) {
160 rc = dev0->read(buffer, sizeof(buffer));
162 if (dev1 == dev) {
163 rc = dev1->read(buffer, sizeof(buffer));
166 if (rc > 0) {
167 std::cout << "received: " << buffer << "\n";
170 return 0;