Very Simple Cross-Platform Library
Very Simple Cross-Platform Library Documentation

Introduction

VSCPL is a simple library containing some useful classes related to thread handling and I/O for POSIX and Windows systems. It is intended to be extended eventually, but has been written chiefly for the X-Plane Remote Access plugin and its client libraries, which is reflected by the rather minimal functionality.

To avoid namespace clashes, the classes of the library are put in the hu::varadiistvan::scpl namespace or children of it.

Threading

The usual thread class and some threading primitives are provided: mutexes and conditional variables.

Input/Output

The I/O parts are somewhat generic, but currently support communication via so-called "local" sockets, which are Unix sockets on POSIX and named pipes on Windows. Of course, a common interface is provided, so the actual details are hidden.

Input/output handling is based on the notion of a thread waiting for one or more I/O events and then handling them. For this purpose an instance of the hu::varadiistvan::scpl::io::Waiter class should be created and then this instance should be passed to the instances of other classes. Waiting can be accomplished by calling its wait function and optionally passing a timeout.

A local server socket can be created by instantiation the hu::varadiistvan::scpl::io::LocalServerSocket class. Besides the waiter, it is given a name. It is ensured that if several users of the computer run programs using the same name, each will see their own socket.

A server socket needs to accept incoming connections. For this purpose and acceptor instance is used, which can be acquired by calling the getAcceptor function. To check, if there is an incoming connection, call the acceptor's accept function. It returns immediately with a boolean telling whether there is a connection. If so, you should call the getSocket function to get the socket representing the connection. If there is no incoming connection, you can wait for one using the waiter. Note, that accept should be called once unsuccessfully before trying to wait, otherwise it is not guaranteed that waiting will finish if a new connection comes in.

LocalAcceptor is a subclass of Failable, which can be used to check if some error occured. If accept returns false, it should be check if the failure of accepting is due to an error, or is caused simply by no client wanting to connect.

A local client socket is created by instantiation hu::varadiistvan::scpl::io::LocalClientSocket. It receives the same name as the server socket to which we want to connect. Similarly to accepting a connection on a server socket, the client socket's getConnector should be called to retrieve a LocalConnector instance. Its connect member function should be called to initiate the connection. If it returns true, the connection has succeeded. Otherwise one can wait using the socket's waiter for the connection to succeed. If waiting finishes, connect can be called again to check, if the connection has been established. In case of a false return value, the error condition should be checked here as well.

Once the connection has been established, the actual communication can begin. Both LocalSocket and LocalClientSocket are subclasses of BufferedStream. It can be used to acquire an instance of ReadingBuffer for reading, and an instance of WritingBuffer for writing.

To read, call the read function. It returns true, if reading has succeeded, false otherwise which may indicate an error condition or simply the fact that nothing has been received yet, in which case the program can wait using the Waiter. If data has been read, it can be accessed by the buffer's methods inherited from Buffer. Before reading again, the buffer's reset function should be called, otherwise read just returns true and nothing happens.

To write, put data into the writing buffer and call its write function. If all data in the buffer could be written, it returns true, otherwise false, which, again, might indicate error, but also the fact that not all data could be written. In this case, the program can wait, and then retry writing again, and do this until finally write returns true. If writing succeeds, the buffer is reset automatically, and one can put new data into it.

The facilities described above provide for asynchronous, event-based stream handling, which is perfect for programs or threads that should work with several streams, timeouts and possibly other events simultaneously. This is, however, not always the case. It might very well be that one thread is dedicated to just deal with one stream only or at least with one stream at a time. In such a case this hocus-pocus with always (re)trying operations and waiting can be very cumbersome. It would be much better to just call a function to read or write, which would return only, if the requested operation has completed.

This is provided by the hu::varadiistvan::scpl::io::BlockingStream class. Its constructor receives and instance of BufferedStream, and it provides blocking read, write and flushing operations. It also has a interrupt function, which can be called from another thread. If the stream is blocking on an operation, that operation will then return with false, and the interrupted condition can the be checked.

BlockingStream has a subclass called DataStream, which can be used to read and write values of the primitive data types. The endianness is that of the processor, i.e. no conversion is made to a common one.