diff -r 0000000000000000000000000000000000000000 -r 8edbcdac0d392b0987c2d1c24e6a17839bae6840 LICENSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE Wed May 08 23:49:28 2013 -0500
@@ -0,0 +1,34 @@
+
+
+ LICENSE
+
+
+ C++ Portable Types Library (PTypes)
+
+ Copyright (C) 2001-2007 Hovik Melikyan
+
+ http://www.melikyan.com/ptypes/
+
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+
+ Hovik Melikyan
+ h@melikyan.com
+
diff -r 0000000000000000000000000000000000000000 -r 8edbcdac0d392b0987c2d1c24e6a17839bae6840 Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile Wed May 08 23:49:28 2013 -0500
@@ -0,0 +1,36 @@
+#
+#
+# C++ Portable Types Library (PTypes)
+# Version 2.1.1 Released 27-Jun-2007
+#
+# Copyright (C) 2001-2007 Hovik Melikyan
+#
+# http://www.melikyan.com/ptypes/
+#
+#
+#
+# Makefile for all Unix platforms, places the library in lib/,
+# the shared library in so/. Also builds the test program
+# src/ptypes_test and the sample program bin/wshare.
+#
+
+UMAKEFILE=Makefile.`uname`
+
+all:
+ cd src ; make -f $(UMAKEFILE) all
+ cd wshare ; make -f $(UMAKEFILE) all
+
+clean:
+ cd src ; make -f $(UMAKEFILE) clean
+ cd wshare ; make -f $(UMAKEFILE) clean
+
+clean-src:
+ cd src ; make -f $(UMAKEFILE) clean-src
+ cd wshare ; make -f $(UMAKEFILE) clean-src
+
+install:
+ cd src ; make -f $(UMAKEFILE) install
+
+uninstall:
+ cd src ; make -f $(UMAKEFILE) uninstall
+
diff -r 0000000000000000000000000000000000000000 -r 8edbcdac0d392b0987c2d1c24e6a17839bae6840 doc/async.examples.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/async.examples.html Wed May 08 23:49:28 2013 -0500
@@ -0,0 +1,204 @@
+
+
Example 1. This simple example shows the use of mutex objects to safely
+perform calculation that involves more than one global variable. Note that using
+the rwlock class instead of mutex
+can improve performance by allowing multiple threads call avg_get()
+at once.
+Example 2. A multithreaded TCP server that uses the jobqueue
+class to maintain a thread pool - a fixed list of reusable threads that receive
+job `assignments' from a queue. The code below can be used as a template for a
+multithreaded network server.
+
+
#include <ptypes.h>
+
+#include <ptime.h>
+#include <pasync.h>
+#include <pinet.h>
+
+USING_PTYPES
+
+
+const int testport = 8085;
+const int maxthreads = 30;
+const int maxtoken = 4096;
+
+const int MSG_MYJOB = MSG_USER + 1;
+
+
+class myjobthread: public thread
+{
+protected:
+ int id;
+ jobqueue* jq;
+ virtual void execute();
+public:
+ myjobthread(int iid, jobqueue* ijq)
+ : thread(false), id(iid), jq(ijq) {}
+ ~myjobthread() { waitfor(); }
+};
+
+
+class myjob: public message
+{
+public:
+ ipstream* client;
+ myjob(ipstream* iclient)
+ : message(MSG_MYJOB), client(iclient) {}
+ ~myjob() { delete client; }
+};
+
+
+void myjobthread::execute()
+{
+ bool quit = false;
+ while (!quit)
+ {
+ // get the next message from the queue
+ message* msg = jq->getmessage();
+
+ try
+ {
+ switch (msg->id)
+ {
+ case MSG_MYJOB:
+ {
+ ipstream* client = ((myjob*)msg)->client;
+
+ // read the request line
+ string req = lowercase(client->line(maxtoken));
+ if (req == "hello")
+ {
+ // send our greeting to the client
+ client->putline("Hello, " + iptostring(client->get_ip()) + ", nice to see you!");
+ client->flush();
+
+ // log this request
+ pout.putf("%t greeting received from %a, handled by thread %d\n",
+ now(), long(client->get_ip()), id);
+ }
+ client->close();
+ }
+ break;
+
+ case MSG_QUIT:
+ // MSG_QUIT is not used in our example
+ quit = true;
+ break;
+ }
+ }
+ catch(exception*)
+ {
+ // the message object must be freed!
+ delete msg;
+ throw;
+ }
+ delete msg;
+ }
+}
+
+
+void servermain(ipstmserver& svr)
+{
+ jobqueue jq;
+ tobjlist<myjobthread> threads(true);
+
+ // create the thread pool
+ int i;
+ for(i = 0; i < maxthreads; i++)
+ {
+ myjobthread* j = new myjobthread(i + 1, &jq);
+ j->start();
+ threads.add(j);
+ }
+
+ ipstream* client = new ipstream();
+
+ pout.putf("Ready to answer queries on port %d\n", testport);
+
+ while(true)
+ {
+ svr.serve(*client);
+
+ if (client->get_active())
+ {
+ // post the job to the queue; the client object will be freed
+ // automatically by the job object
+ jq.post(new myjob(client));
+ client = new ipstream();
+ }
+ }
+}
+
+
+int main()
+{
+ ipstmserver svr;
+
+ try
+ {
+ // bind to all local addresses on port 8085
+ svr.bindall(testport);
+
+ // enter an infinite loop of serving requests
+ servermain(svr);
+ }
+ catch(estream* e)
+ {
+ perr.putf("FATAL: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ return 0;
+}
+
PTypes provides a minimal set of utility objects for creating complex multithreaded
+and event-driven applications. In addition to threads
+and inter-thread synchronization primitives semaphore,
+mutex, rwlock and
+trigger the library also allows you to create
+message queues (msgqueue) typically used in
+windowed or other event-driven environments. The jobqueue
+class helps to build multithreaded server and robot applications with a pool of
+reusable thread objects.
+
The implementation and interfaces of threads and synchronization objects vary
+on platforms supported by the library. If an operating system lacks one of the
+features, PTypes implements it using the other primitives present in that OS.
+Please, see <pasync.h> for the implementation
+cross-reference of the synchronization primitives.
+
These interfaces except the atomic functions are available only in multithreaded
+versions of the library.
+
The multithreading classes are declared in <pasync.h>.
The jobqueue class implements a thread-safe list
+of objects of type message
+or any derivative class. Jobqueue supports posting to
+and reading from the list from multiple threads simultaneously.
+
The jobqueue class can help to build multithreaded
+server/robot applications by maintaining a pool of reusable threads that receive
+job 'assignments' from a queue. This threading model can be faster compared to
+applications that create and destroy separate thread objects for each task. See
+Examples for a full-featured server template
+with a thread pool.
+
jobqueue::jobqueue(int limit = DEF_QUEUE_LIMIT) constructs
+a job queue object. Limit specifies the maximum number
+of messages this queue can hold. If the limit is reached, the next thread that
+posts a message will wait until the queue becomes available again. In this version
+the default for limit is 5000.
+
void jobqueue::post(message* msg) adds a message to
+the queue. Msg can be an object of class message
+or any derivative class. The message object should always
+be created dynamically using operator new. The messages
+in the queue are processed in order they were posted, i.e. on first-in-first-out
+basis.
+
void jobqueue::post(int id, pintptr param = 0) creates
+a message object using id and param
+and calls post(message*).
+
void msgqueue::posturgent(message* msg) posts a message
+object "out of turn", i.e. this message will be processed first. The
+messages posted through this method are processed on first-in-last-out basis.
+post() and posturgent() can
+be used alternately on the same queue.
+
void jobqueue::posturgent(int id, pintptr param = 0)
+creates a message object using id and param
+and calls posturgent(message*).
+
message* jobqueue::getmessage(int timeout = -1) retrieves
+the next message from the queue or waits if there are no messages available. The
+timeout parameter specifies the timeout value in milliseconds.
+If timeout is -1 (the default) getmessage()
+will wait for a message infinitely. This function returns a message object, or
+otherwise NULL if the time specified in timeout
+has elapsed. NOTE: the message object returned by this function must be
+freed with operator delete. It is safe to call getmessage()
+concurrently.
+
int jobqueue::get_count() returns the number of messages
+currently in the queue.
+
int jobqueue::get_limit() returns the queue limit
+set by the constructor.
A message object or an object of a derived class
+can be used to post data to message queues (msgqueue)
+or job queues (jobqueue). Messages are distinguished
+by a ID that are defined in your application. The user message IDs can be in the
+range 0 through INT_MAX. Negative
+values are reserved for internal use by the library.
+
A simple message can also contain an additional parameter of type pintptr,
+which is an integer with a size equal to the size of a pointer on the given platform.
+For more complex message structures you can define classes derived from message
+ - the message queue manager can work with any descendant class as well.
+
message::message(int id, pintptr param = 0) constructs
+a message object and assigns id and param
+fields. param is an additional field that can be used
+in your application at your own discretion.
+
int message::id -- this field contains a message ID
+assigned through the constructor.
+
pintptr message::param contains the optional parameter.
+
pintptr message::result -- through this field a message
+handler can return some simple answer to the sender of this message.
The msgqueue class implements a queue of message
+objects and is typically used to synchronously exchange data in a multithreaded
+environment.
+
The thread which created the msgqueue object can
+retrieve messages from the queue using one of run(),
+processmsgs(), processone().
+Applications always define a class derived from msgqueue
+and override the pure virtual method msghandler(). The
+overridden method receives message structures and performs
+appropriate actions depending on the message ID. Any other thread or even multiple
+threads in your application can send or post messages to the given queue using
+post(), posturgent() or send().
+
Msgqueue can serve as a synchronization object between
+threads. Unlike semaphores, where both sending and receiving threads can "hang"
+when waiting for readiness of the peer, msgqueue allows
+the sender to send data and immediately continue the execution. The receiver in
+its turn processes messages one by one in the same order as they were posted.
+
+
Threads can not only exchange data through a message queue, but also send simple
+notifications about various events. Message queues can as well be used in single-threaded
+applications with event-driven logic.
+
A simple example of using msgqueue could be a server
+application with multiple threads, each serving one client; the server maintains
+a log file or a table in a database where it records various events. To record
+events synchronously the client threads are sending appropriate messages to the
+main thread. The client threads never waste time, they just post their messages
+and immediately continue their work.
+
IMPORTANT NOTES: (1) a message object should
+always be constructed dynamically, i.e. using operator new;
+(2) a message object is always destroyed by the queue
+manager after it has been processed; (3) a message object
+can be sent and processed only once.
+
A slower but more universal alternative to the message queue is local pipe
+(see infile::pipe()).
+
msgqueue::msgqueue(int limit = DEF_QUEUE_LIMIT) constructs
+a message queue object. It doesn't matter which thread is creating this object,
+but later only one thread can process the queue and handle messages. Limit
+specifies the maximum number of unhandled messages this queue can hold. If the
+limit is reached, the next thread that posts a message will wait until the queue
+becomes available again. In this version the default for limit
+is 5000.
+
void msgqueue::processone() processes one message
+from the queue. This method may "hang" if no messages are available.
+processone() calls the overridden msghandler()
+and then destroys the message object.
+
void msgqueue::processmsgs() processes all messages
+in the queue and returns to the caller. If there are no messages in the queue,
+processmsgs() returns immediately. Each message is processed
+as described for processone().
+
void msgqueue::run() enters an infinite loop of message
+processing which can only be terminated by sending or posting a special message
+MSG_QUIT (e.g. post(MSG_QUIT)).
+Each message is processed as described for processone().
+
void msgqueue::post(message* msg) adds a message to
+the queue. Msg can be an object of class message
+or any derivative class. The message object should always
+be created dynamically using operator new. The messages
+in the queue are processed in order they were posted, i.e. on first-in-first-out
+basis. post() can be called from any thread, including
+the thread owning the queue.
+
void msgqueue::post(int id, pintptr param = 0) creates
+a message object using id and param
+and calls post(message*).
+
void msgqueue::posturgent(message* msg) posts a message
+object "out of turn", i.e. this message will be processed first. The
+messages posted through this method are processed on first-in-last-out basis.
+post() and posturgent() can
+be used alternately on the same queue. Like post(),
+this method can be called from any thread.
+
void msgqueue::posturgent(int id, pintptr param = 0)
+creates a message object using id and param
+and calls posturgent(message*).
+
pintptr msgqueue::send(message* msg) calls the message
+handler directly, by-passing the queue. If the sender is the same as the thread
+owning the queue, send() simply calls msghandler().
+Otherwise, if the sender is a concurrent thread, send()
+enters an effective wait state until the message is processed by the owner thread.
+The return value is the value of result in the message
+object. In both cases the message is destroyed upon return from send().
+
pintptr msgqueue::send(int id, pintptr param = 0)
+creates a message object using id and param
+and calls send(message*). The return value is the value
+of result in the message object.
+
int msgqueue::get_count() returns the number of messages
+currently in the queue.
+
int msgqueue::get_limit() returns the queue limit
+set by the constructor.
+
virtual void msgqueue::msghandler(message& msg) this
+pure virtual method should be overridden to provide application-specific message
+handling functionality. msghandler() usually checks
+the message ID through a switch statement. If the message
+ID is unknown to the application, defhandler() should
+be called. The message object msg CAN NOT be reused
+with post(), posturgent()
+or send(), neither it can be destroyed within the message
+handler. The message handler can assign some value to msg.result
+to return a simple answer to the caller of send().
+
void msgqueue::defhandler(message& msg) is called
+from within user-defined msghandler() when the message
+ID is unknown to the application. defhandler() processes
+some messages internally used by the library, e.g. MSG_QUIT.
Mutex (mutual exclusion) is another synchronization
+object which helps to protect shared data structures from being concurrently accessed
+and modified.
+
Accessing and changing simple variables like int
+concurrently can be considered safe provided that the variable is aligned on a
+boundary "native" to the given CPU (32 bits on most systems). More often,
+however, applications use more complex shared data structures which can not be
+modified and accessed at the same time. Otherwise, the logical integrity of the
+structure might be corrupt from the "reader's" point of view when some
+other process sequentially modifies the fields of a shared structure.
+
To logically lock the part of code which modifies such complex structures the
+thread creates a mutex object and embraces the critical code with calls to enter()
+and leave(). Reading threads should also mark their
+transactions with enter() and leave()
+for the same mutex object. When either a reader or a writer enters the critical
+section, any attempt to enter the same section concurrently causes the thread
+to "hang" until the first thread leaves the critical section.
+
If more than two threads are trying to lock the same critical section, mutex
+builds a queue for them and allows threads to enter the section only one by one.
+The order of entering the section is platform dependent.
+
To avoid infinite locks on a mutex object, applications usually put the critical
+section into try {} block and call leave()
+from within catch {} in case an exception is raised
+during the transaction. The scopelock class provides
+a shortcut for this construct: it automatically locks the specified mutex object
+in its constructor and unlocks it in the destructor, so that even if an exception
+is raised inside the scope of the scopelock object leave()
+is guaranteed to be called.
+
More often applications use a smarter mutual exclusion object called read/write
+lock -- rwlock.
+
PTypes' mutex object encapsulates either Windows
+CRITICAL_SECTION structure or POSIX mutex object and implements the minimal set
+of features common to both platforms. Note: mutex may not be reentrant
+on POSIX systems, i.e. a recursive lock from one thread may cause deadlock.
+
mutex::mutex() creates a mutex object.
+
void mutex::enter() marks the start of an indivisible
+transaction.
+
void mutex::leave() marks the end of an indivisible
+transaction.
+
void mutex::lock() is an alias for enter().
+
void mutex::unlock() is an alias for leave().
+
scopelock::scopelock(mutex& m) creates a scopelock
+object and calls enter() for the mutex object m.
+
scopelock::~scopelock() calls leave()
+for the mutex object specified during construction and destroys the scopelock
+object.
Rwlock (read/write lock) is a variation of mutex
+with a possibility to control critical sections more efficiently. Unlike the simple
+mutex, rwlock allows multiple reader threads
+to enter the critical section, and only one writer thread at a time. Reading
+and writing in this context means access to a resource or compound data shared
+between threads. Reader threads must lock the critical section with rdlock()
+and the writers must lock it with wrlock(). Both leave
+the critical section with unlock().
+
This class incorporates POSIX rwlock interface on UNIX and library's own implementation
+on Windows and MacOS X. When using the rwlock class
+on Linux, you need to define a symbol _GNU_SOURCE either
+in the command line, or anywhere in your source before including any system headers.
+
Analogously to scopelock (see mutex),
+scoperead and scopewrite are
+fully-inline'd utility classes provided for exception-safe locking of operator
+blocks.
void rwlock::rdlock() locks the object for reading.
+Multiple threads can enter the critical section through rdlock(),
+however, if the object is already locked for writing, all reader threads wait
+until the writer leaves the critical section.
+
void rwlock::wrlock() locks the object for writing.
+If there are readers inside the critical section, the writer waits until all threads
+leave and unlock the object. Only one writer at a time can enter the critical
+section.
+
void rwlock::unlock() unlocks the object. Both readers
+and writers must use this method when leaving the critical section.
+
scoperead::scoperead(rwlock& rw) creates a scoperead
+object and calls rdlock() for the object rw.
+
scoperead::~scoperead() calls unlock()
+for the rwlock object specified during construction
+and destroys the scoperead object.
+
scopewrite::scopewrite(rwlock& rw) creates a scopewrite
+object and calls wrlock() for the object rw.
+
scopewrite::~scopewrite() calls unlock()
+for the rwlock object specified during construction
+and destroys the scopewrite object.
Semaphore is a special helper object with very simple logic which is typically
+used to synchronize the execution of concurrent threads. A semaphore object can
+be considered as an integer value which has one additional feature: if its value
+is 0, an attempt to decrement this value will cause the calling thread to "hang"
+until some other thread increments it. "Hanging" on the semaphore means
+entering effective wait state and consuming no or little CPU time, depending on
+the operating system.
+
One example showing the use of semaphores is when one thread needs to send
+data (e.g. through a buffer) to another thread. In multithreading environments
+there is no guarantee in which order two threads will come to the point where
+the first thread is filling the data buffer and the other thread is reading it.
+Therefore, these two threads need to synchronize execution at the exchange point.
+Semaphore's logic for this case is fairly simple: the reader thread calls wait()
+before reading the buffer and "hangs" if the semaphore is not
+yet signaled. The writer thread calls post() after filling the buffer with
+data and thus signals the reader thread that the data buffer is ready. This schema
+ensures that the data buffer will be read by the second thread only when the data
+is actually ready.
+
If the data exchange cycle is iterative you will have to make sure also that
+the buffer is not filled twice before the reading thread takes the first data
+chunk. In this situation another semaphore should be created with reverse logic:
+the semaphore is set to signaled state when the reading thread has taken the first
+data chunk and is ready to take the second chunk. The writing thread, in its turn,
+waits on this semaphore to make sure the buffer is ready for the successive data
+chunk.
+
In more complex applications when many threads need to exchange data with each
+other or with the main application thread, message queues can be used instead
+of semaphores. The message queue object itself is another example of using semaphores
+(please see pmsgq.cxx source file).
+
You can use semaphores when your application needs to limit the number of concurrently
+running threads of the same type. A typical web robot application, for example,
+creates a new thread for each download process. To limit the number of threads
+the application creates a semaphore with the initial value equal to the maximum
+allowed number of threads. Each new thread decrements the semaphore by calling
+wait() and then increments it with post()
+upon termination. If the maximum allowed number of threads is reached, the next
+thread calling wait() will "hang" until one
+of the running threads terminates and calls post().
+
PTypes' semaphore object encapsulates either Windows
+semaphore or an implementation based on POSIX synchronization primitives. This
+object implements the minimal set of features common to both Windows and POSIX
+semaphores. Besides, semaphores can not be shared between processes on some operating
+systems, thus limiting the use of PTypes' semaphores to one process.
+
PTypes' timedsem adds timed waiting feature to
+the simple semaphore. This class has an interface compatible with the simple semaphore
+with one additional function - wait(int) with timer.
+The reason this feature is implemented in a separate class is that not all platforms
+support timed waiting. Wherever possible, PTypes uses the system's native sync
+objects, or otherwise uses its own implementation based on other primitives. Note
+that timedsem can be used both for infinitely waiting
+and timed waiting; it is, however, recommended to use simple semaphore
+if you are not going to use timed waiting.
+
As an example of using timedsem see implementation
+of thread 'relaxing' mechanism in include/pasync.h
+and src/pthread.cxx.
+
semaphore::semaphore(int initvalue) constructs a
+semaphore object with the initial value initvalue.
+
void semaphore::wait() decrements the semaphore's
+value by 1. wait() can enter effective wait state if
+the value becomes -1, in which case the thread will "hang" until some
+other thread increments the value by calling post().
+
void semaphore::post() increments the semaphore's
+value by 1. post() can release some other thread waiting
+for the same semaphore if its value was -1.
+
void semaphore::signal() is an alias for post().
+
timedsem::timedsem(int initvalue) constructs a
+semaphore object with an interface fully compatible with (but not inherited from)
+semaphore. This class has one additional method for
+timed waiting (see below).
+
bool timedsem::wait( [ int milliseconds ] ) decrements
+the semaphore's value by 1 and enters effective wait state if the value becomes
+-1. Unlike simple wait() this function will 'wake' and
+return if the time specified in milliseconds has elapsed,
+in which case the function returns false. If the semaphore was signaled with post()
+or signal() before the time elapses this function returns
+true. If milliseconds is omitted or is -1 the function
+will wait infinitely, like simple wait().
Create a descendant of thread to represent an execution
+thread in a multithreaded application. All threads are running within the framework
+of the parent process and share the same global variables. Each thread, however,
+has its own stack. The execution of a thread can be started either from the main
+process or from another thread. Further, the newly launched thread executes its
+code concurrently.
+
Multithreading can significantly improve your application when a process consumes
+time by waiting for data from some communication device, a slow storage device
+or user input. When such pieces of code run concurrently the CPU is loaded more
+efficiently. Also, many networking services use threading to serve multiple users
+simultaneously.
+
When threads concurrently access and modify shared data structures precautions
+should be taken to preserve logical integrity of these data structures. Synchronization
+between threads is performed using either of: semaphore,
+mutex, rwlock,
+trigger or msgqueue.
+
thread::thread(bool autofree) creates a thread object,
+but does not run it. If autofree is true
+the thread will destroy itself upon termination. Note, that you can use autofree
+only for dynamically allocated thread objects. A variable that holds a pointer
+to a dynamic autofree thread object can be considered
+invalid after a call to start().
+
thread::~thread() destroys the thread object. Usually
+both the constructor and the destructor for non-autofree threads are called from
+a different context. In contrary, autofree threads call their destructors from
+their own context.
+
void thread::start() runs the thread. The overridden
+virtual method execute() is called asynchronously. Start()
+itself returns immediately. Each thread object can be run only once.
+
void thread::waitfor() waits for the thread to terminate.
+This function is called when a thread needs to synchronize its execution with
+the completion of the target non-autofree thread. For non-autofree threads this
+method must be called at least once and within the scope of a thread that
+called start(). For convenience, you can place a call
+to waitfor() in the overridden destructor. waitfor(),
+however, can not be called for autofree threads.
+
void thread::signal() sets signaled attribute to true
+and possibly wakes up the thread if in a `relaxed' state (see relax()
+below). signal() is usually called from a different
+thread to let the given thread know that the execution should be terminated as
+soon as possible. If the given thread performs an iteration one of the conditions
+of leaving the loop should be (!get_signaled()). Signal()
+can be called only once for a thread object.
+
pthread_id_t thread::get_id() returns the thread
+ID assigned by the operating system. This ID can then be used in call to pthrequal().
+
bool thread::get_running() returns true
+if the given thread object is running. This property is never set to false
+once the thread started. To check whether the thread finished its job, i.e. returned
+from execute(), use get_finished().
+
bool thread::get_finished() returns true
+if thread has already terminated its execution, or, in other words, has left execute().
+
bool thread::get_signaled() returns true
+if the thread object is in signaled state, i.e. signal()
+has been called from a concurrent thread. See also signal().
+
virtual void thread::execute() -- this pure virtual
+method should be overridden in the descendant class to implement the functionality
+of your thread. execute() can be viewed as main()
+for your mini-process. Typically, you create a class descendant from thread
+and override at least two virtual methods: execute()
+and cleanup().
+
virtual void thread::cleanup() -- this pure virtual
+method should be overridden in the descendant class. cleanup()
+is called either when the thread terminates normally or when an exception is raised
+within the thread's context. When implementing this method you might want to clean
+up any memory and other resources allocated by your thread object. cleanup()
+is guaranteed to be called once upon termination of the thread. To properly shut
+down the thread, avoid using operations that can cause exceptions in cleanup().
+
bool thread::relax(int milliseconds) is a protected
+member function which can be called only from within the overridden execute()
+method. This function suspends the execution of the thread until either of: the
+specified amount of time is elapsed, in which case the function returns false,
+or signal() is called from a concurrent thread, in which
+case relax() returns true. If parameter milliseconds
+is -1 the function will wait infinitely. The relax/signal mechanism is useful
+for threads doing some job that requires taking periodic actions in the background.
Trigger is a simple synchronization object typically
+used to notify one or more threads about some event. Trigger
+can be viewed as a simplified semaphore, which has only two states and does not
+count the number of wait's and post's. Multiple threads can wait for an event
+to occur; either one thread or all threads waiting on a trigger can be released
+as soon as some other thread signals the trigger object.
+Auto-reset triggers release only one thread each time post()
+is called, and manual-reset triggers release all waiting threads at once. Trigger
+mimics the Win32 Event object.
+
trigger::trigger(bool autoreset, bool initstate) creates
+a trigger object with the initial state initstate. The
+autoreset feature defines whether the trigger object
+will automatically reset its state back to non-signaled when post()
+is called.
+
void trigger::wait() waits until the state of the
+trigger object becomes signaled, or returns immediately if the object is in signaled
+state already.
+
void trigger::post() signals the trigger object. If
+this is an auto-reset trigger, only one thread will be released and the state
+of the object will be set to non-signaled. If this is a manual-reset trigger,
+the state of the object is set to signaled and all threads waiting on the object
+are being released. Subsequent calls to wait() from
+any number of concurrent threads will return immediately.
+
void trigger::signal() is an alias for post().
+
void trigger::reset() resets the state of the trigger
+object to non-signaled.
The atomic functions pexchange(), pincrement()
+and pdecrement() can be used in place of mutex locking
+in some simple situations. A typical usage of pexchange()
+in a multithreaded environment could be, for example, freeing a dynamic object
+and assigning NULL to the pointer atomically to prevent concurrent threads
+from freeing the same object more than once.
+
It is sometimes necessary to increment or decrement some shared counter and
+atomically compare it with some value. For example, you have a shared resource
+and you keep track of its usage by maintaining a reference counter. When this
+counter reaches 0 you want to free the shared resource. To avoid conflicts between
+concurrent threads you need to decrement the counter and atomically compare it
+with 0. In this situation you can use pdecrement() instead
+of time-consuming mutex locking. (For additional notes, see Portability
+and performance issues.)
+
int pexchange(int* target, int value) -- atomically
+exchanges two int values.
+
void* pexchange(void** target, void* value) -- atomically
+exchanges two pointers.
+
int pincrement(int* target) -- atomically increments
+the value of *target and returns the new value.
+
int pdecrement(int* target) -- atomically decrements
+the value of *target and returns the new value.
+
template <class T> T* tpexchange(T** target, T* value)
+-- is equivalent to pexchange() that adds compile-time
+type checking. Use this template to check the correspondence of pointer types
+of target and the returning value.
+
void psleep(unsigned milliseconds) -- suspends execution
+for the specified amount of time in milliseconds.
+
pthread_id_t pthrself() -- returns the thread ID of
+the calling thread.
+
bool pthrequal(pthread_id_t id) -- checks whether
+the calling thread has the given thread id.
Compilation problems solved on *BSD systems (64-bit seek issue, Bug report)
+
Several MacOS X compilation problems solved (socklen_t, libtool)
+
MSVC project files are now in the new VC7+ format (.sln, .vcproj)
+
Dropped support for BSDi, CygWin and also the Borland C++ compiler.
+
+
Version 2.1.0
+
+
64-bit file positioning is now fully supported in stream classes. New methods tellx() and seekx() were added for this. Old methods tell() and seek() still return 32-bit offsets and may raise an exception if the returned value doesn't fit.
+
In addition to Intel and PowerPC processors, atomic functions for SPARC were rewritten in the assembly language, too (previously a mutex hash table was used).
+
All socket interfaces now have a virtual function sockopt() that can be overridden in descendant classes. This function is always called immediately after a socket descriptor is created, so that you can set up any additional socket options via a system call setsockopt().
+
semaphore::wait() now handles EINTR which may occur if a process catches a signal. (Bug report)
+
+
Version 2.0.3
+
+
Bug fixed that affected pack() and grow() methods in list classes (Bug report)
Bug fixed in instm::seek() with IO_CURRENT (Bug report)
+
Bug fixed in outmemory::seek() for unlimited streams (Bug report)
+
Bug fixed in thread::~thread() for thread objects that never ran (Bug report)
+
Visual C project files are now in DOS text format. (Problem report)
+
PTypes-1.8 compatibility code removed (1.9-style lists are still supported)
+
Author's email has changed, please see Introduction.
+
+
Version 2.0.2
+
+
The memory corruption problem with the LinuxThreads library fixed (Problem
+report).
+
Added versions of ipmessage::receive() that also
+return the source IP address.
+
Added new properties and methods to inmemory and
+outmemory classes to allow reusability of objects of
+these types. Also cleaned up implementation of outmemory
+so that the increment property is no longer needed.
+
Added textmap::getkey() method.
+
Added const versions of operator[] for string
+and tpodlist.
+
Bug fixed in pexchange() on PowerPC with gcc 3.3
+(Bug
+report)
The list class/template family has been redesigned and expanded with new,
+more flexible interfaces. An overview is given in the Introduction
+to lists. The old interfaces are preserved in the headers for backward compatibility.
+
Ported the library to Cygwin.
+
'make install' copies the header files and the binaries
+to default system locations.
+
Jobqueue and msgqueue
+are now protected from overflows by an extra semaphore. If the number of unhandled
+messages in a queue reaches limit (a parameter set through
+the constructor) the post/send operation waits on a semaphore until the queue
+becomes available again.
+
Windows DLLs now called: ptypes20.dll and ptypes20g.dll, generted by MSVC
+and GCC respectively.
+
Added __stdcall (ptdecl
+macro) modifiers for all public functions in the library to be independent from
+the compiler command-line options on Windows.
+
Variant arrays have been optimized for memory usage.
+
All classes in the library are now either copyable or non-copyable explicitly.
+
The last parameter of copy() and del()
+for strings is now optional.
A new class jobqueue has been added. As
+shown in the multithreading examples, it helps to easily implement a thread-pool
+model.
+
Multithreading examples have been re-written.
+Example 2 is now a full-featured multithreaded server template with a thread pool.
+
Atomic functions for PowerPC have been implemented in the assembly language.
+
UNIX shared object naming style has been changed. The shared library is now
+named libptypes.so.19 (libptypes.19.dylib on MacOS). The shared object now 'remembers'
+its real name (with the -soname option) for correct linking of user applications.
+(Bug
+report)
+
Windows/MSVC DLL has been renamed to ptypes19.dll because of incompatible
+changes in the library structures. The DLL built by Dev-C++ has been renamed to
+ptypes19dc.dll for the same reason and also to avoid clashing with MSVC-generated
+DLL.
+
The multithreaded version of the dynamic string class has been improved.
+
A minor performance fixup in msgqueue::send() -
+the semaphore is no longer allocated dynamically.
+
The exceptobj class has been renamed to exception,
+tsemaphore - to timedsem.
+The old names are typedef'ed for compatibility with older versions of the library.
+
The message class now uses a portable typedef pinptr
+for the param and result fields.
+This allows to use these fields both as integers and pointers when necessary.
+The id field can now be in the range 0
+to INT_MAX; the internal value MSG_QUIT
+is now a negative value.
+
Bind() and bindall() now
+return values that can be used in call to poll() and
+serve() for ipserver objects.
+
setlength(string&) now returns a char*
+value.
+
A bug has been fixed in the trigger class. (Bug
+report)
+
The contains() family of functions has been fixed
+to correctly handle empty test strings. (Bug
+report)
+
The ins() family of functions has been fixed to
+correctly handle values of at beyond the last character
+of a string. (Bug
+report)
+
A bug has been fixed in the internal function psockname()
+that caused get_myport() methods (ipstream
+and ipmessage classes) to return an incorrect value.
+(Bug
+report)
+
Compiler warning (and a potential problem) has been fixed on 64-bit systems.
+(Problem
+report)
+
+
Version 1.8.3
+
+
Public header files are now included in the documentation in browsable form
+(see Header files).
+
Delete notification mechanism for components, previously undocumented, now
+described in section Unknown & Component.
+
Variant-to-string typecast problem has been fixed that caused compilation
+errors with GCC 3.3 (Problem
+report)
+
A problem has been solved that allowed template lists to accept any class
+type, even if it's not derived from unknown. (Problem
+report)
+
A better explanation of the librarie's policies and philosophy is given in
+section 'Why use PTypes?' (see Introduction).
+
+
Version 1.8.2
+
+
The library has been ported to BSD/OS (BSDI). There is also limited support
+for OpenBSD and NetBSD without makefiles.
+
The library now compiles with Visual C/C++ 7.1 (.NET) after considerable changes
+in <ptypes.h>
+
A bug has been fixed in instm::token() that caused
+segfaults. (Bug
+report)
+
A problem has been fixed in phost*() functions that
+caused segfaults on SuSE Linux. (Bug
+report)
+
A bug has been fixed in wshare: the plus sign '+' should not be decoded as
+space in URL components other than query. (Bug
+report)
+
+
Version 1.8.1
+
+
The following functions have been fixed to be thread-safe on UNIX systems:
+phostbyname(), phostbyaddr(),
+phostcname(), nowstring(),
+and also now() and tzoffset()
+on MacOS X. (Bug
+report 1, Bug
+report 2)
+
+
Version 1.8.0
+
+
The library now compiles in 3 versions: static single-threaded, static multithreaded
+and dynamic (shared) multithreaded (see Compiling).
+On some platforms single-threaded dynamic strings can be twice as faster than
+multithreaded.
+
New format specifiers for outstm::putf()
+introduced: %a for IP addresses and %t
+for timestamps.
New conversion functions added stringtoie() and
+stringtoue() (see String
+conversion).
+
A bug has been fixed in tsemaphore::wait(). This
+bug affected all platforms except Windows. (Bug
+report)
+
SO_REUSEADDR problem fixed on Windows. (Bug
+report)
+
A bug has been fixed in stringtoi(). (Bug
+report)
+
+
Version 1.7.6
+
+
The library now compiles with Dev-C++/MinGW on Windows. Dev-C++ project files
+have been created for building the static and shared libraries, as well as the
+demo and testing programs.
+
A new file output class called logfile
+has been created as a result of a discussion around this bug
+report.
+
Wshare can now downgrade process privileges (setuid/setgid)
+on UNIX. Also, file handling and server status report has moved to separate optional
+modules mod_file.cxx and mod_wstat.cxx.
+
A new section has been added in the documentation: Resources.
+This page can be accessed only on-line on the master server, since changes here
+may appear more frequently than in the library documentation.
A bug has been fixed in strmap::strmap() constructor
+(Bug
+report).
+
Thread creation routine has been changed on Windows to ensure thread-safety
+of system static variables (Bug
+report).
+
+
Version 1.7.5
+
+
A tv_usec-related bug fixed in all socket/pipe waiting
+and polling routines. Non-integral timeout values were treated incorrectly in
+the previous versions of the library. (Bug
+report)
+
Fixed return type for tstrmap::operator[]. (Bug
+report)
+
instm::putback() added.
+
+
Version 1.7.4
+
+
A new class unit has been added. Unit is a mini-process
+with its own main() and input/output 'plugs'; several
+unit objects within the framework of one application can be connected to form
+pipes, like processes in the UNIX shell.
+
A new method for creating local pipes, infile::pipe(),
+has been added.
+
Null output stream object has been added as a static variable pnull.
+
__ptypes_version and the DLL version resource were
+not incremented in the previous release. Now they both show 1.7.4.
+
outmemory::get_strdata() added.
+
+
Version 1.7.3
+
+
Wrapper templates for objlist, strlist
+and strmap have been created. These templates help to
+avoid extra (dangerous) pointer typecasts and provide better compile-time type
+checking when using lists with objects derived from unknown.
+See introduction to Lists.
+
A new class template compref has been added that
+implements a 'smart' pointer to a reference-counted component
+object.
+
Function template tpexchange() has been added
+as a type-safe wrapper for pexchange().
Fixed compilation and linking parameters for Linux and FreeBSD to build the
+shared library correctly (thanks to Alan Eldridge).
+
Bug fixed in wshare/urlutils.cxx: the URL parameters (protocol parameters,
+query string and fragment) were treated incorrectly in previous versions.
+
+
Version 1.7.1
+
+
A new section called "Deploying the shared (dynamic)
+library" is now included in the documentation. It describes in detail
+the procedure of using and deploying the shared (dynamic) library, discusses advantages
+and disadvantages of dynamic linking vs. static linking.
+
PTypes.DLL now contains a version resource. In addition, the library declares
+a global variable __ptypes_version, which can be checked at run-time or during
+installation on UNIX.
+
strlist::compare() is declared as virtual to allow
+alternate sorting algorithms in descendant classes.
+
+
Version 1.7.0
+
+
Variant class implemented. A variant
+variable can hold values of many fundamental types (int, bool, datetime, float,
+string) plus associative arrays of variants and reference-counted pointers to
+objects. Variants can be used for designing interpreters for higher-level languages,
+designing or working with databases and spreadsheets, etc.
+
Message-oriented networking classes (ipmessage
+and ipmsgserver) implemented based on the
+UDP protocol.
+
The library now compiles both as a shared object (DLL on Windows) and a static
+library. The shared object (or DLL) is placed in so/
+when building the library.
+
outstm::putf() is now atomic with respect to multithreading.
+
Ipsocket renamed to ipstream,
+ipserver renamed to ipstmserver.
+The old names are available as typedef's for compatibility.
+
+
Version 1.6.1
+
+
Read/write lock (rwlock) algorithm improved
+to be "fairer" with respect to both readers and writers.
+
Trigger algorithm fixed to be fully compatible
+with Windows event interface.
+
psleep() fixed on Solaris to be reentrant (multithreaded).
Memory stream classes inmemory and outmemory,
+previously undocumented, now described in section Streams.
+
Windows InterlockedXXX calls replaced with internal assembly code to work
+as expected even on Win95.
+
A string-to-int (64-bit) conversion routine added - stringtoi().
+
ipsocket::get_myip() added.
+
Dynamic string assignment and itostring() optimized.
+
Better diagnostics messages for ipserver.
+
+
Version 1.5.3
+
+
Token extraction methods can now optionally limit input and throw en exception
+if the token exceeds the limit. This new feature is targeted to real-world networking
+applications.
Examples for the sockets interface and some other modules rewritten to be
+cleaner and more educational.
+
GCC 3.x compilation problem solved in pinet.h (friend class declaration).
+
+
Version 1.5.2
+
+
Dynamic strings have been optimized and thoroughly tested. Compared to MFC
+CString class, PTypes dynamic strings now show much better performance.
+
Bug fixed in decodedate(). This function worked
+incorrectly for the last day (12/31) of each leap year.
+
+
Version 1.5.1
+
+
Documentation cleanup.
+
A sample compile-time module for wshare, which responds to http://hostname/.about
+requests. Code cleanup in wshare.
+
+
Version 1.5.0
+
+
Wshare is becoming modular/scalable. Custom handlers for a new HTTP method,
+file extension or a nonexistent path can be easily incorporated into wshare at
+compile time. Currently this feature is documented only in the source files.
+
Wshare can show the server status report through http://localhost/.wstat
+
Standard input, output and error devices are declared in <pstreams.h>
+as pin, pout and perr stream objects respectively.
+
New printf-style formatted output method outstm::putf().
+Accepts only a subset of format specifiers common to all platforms.
+
Bug fix in outstm::put().
+
Handle leak on Windows fixed.
+
realloc-related memory leak on UNIX fixed.
+
Bug in string class fixed: s += s was working incorrectly
+in previous versions.
+
+
Version 1.4.1
+
+
The MD5 module is replaced with L. Peter Deutsch's implementation. Please,
+see comments in src/pmd5.cxx.
+
Wshare: code cleanup in various modules; Borland C++ port (wshare/wshare.mak);
+a new option -x to ignore default index files and always
+show directory indexes.
+
Include directives in src/pipserver.cxx and src/pipsocket.cxx
+have been reordered to avoid compiler errors on some Linux systems.
+
+
Version 1.4.0
+
+
A big sample program called wshare is now included in the project. Wshare
+is a simple and fast HTTP/1.1 web server; it demonstrates the full power of PTypes.
+Currently wshare lacks server-side scripting functionality, however, it can be
+used to quickly share local files over the web.
+
Waiting for data with timeout on a socket object is implemented (ipsocket::waitfor()).
+
SIGPIPE is now blocked on UNIX when using PTypes'
+sockets.
+
A new utility function utodatetime() for converting
+UNIX time_t values to PTypes datetime.
+
Atomic exchange for pointers is implemented separately, since on 64-bit platforms
+the size of a void pointer differs from the size of int.
+
A bug fix for autofree threads. Please note that semantics of thread::waitfor()
+has changed (see documentation for details).
+
+
Version 1.3.2
+
+
Ported to MacOS X (Darwin).
+
Important fixes in the thread module (src/pthread.cxx):
+PTypes now ensures proper operation and memory cleanup on all platforms, even
+with buggy implementation of the POSIX threads specs.
+
Compiled under Linux/Alpha, Linux/PowerPC and Linux/Sparc with minor fixes:
+ipaddress type is now castable to unsigned long instead
+of signed long; variables of type ipaddress must be compared with ipnone instead
+of -1.
+
+
Version 1.3.1
+
+
CVS repository setup at SourceForge.net (see the main page for details).
+
Various fixes in the documentation.
+
Fixes in the BCC makefile src/ptypes.mak.
+
+
Version 1.3
+
+
IP socket manipulation classes and utilities, finally!
+
The new datetime type along with time/calendar manipulation
+utilities.
The library now compiles within a namespace "pt" by default.
+
semaphore::post() and semaphore::wait()
+now do not return any values. If these functions fail, a fatal non-recoverable
+error is raised instead of returning an error code to the caller. It is senseless
+to run a multithreaded application if the system fails to operate semaphores.
+
Bug fixed in non-Intel version of pexchange().
+
+
Version 1.2
+
+
The entire library can now be conditionally enclosed within a namespace to
+avoid name conflicts in large projects. By default the library does NOT compile
+within a namespace.
+
Several fixes to improve performance of the dynamic string class, as well
+as to make it thread safe (see introduction to string class).
+
Ported to SunOS. (The library should now compile virtually on any UNIX platform.
+Need some time and access to different computers.)
+
Compiled with Borland C++ 5.5 (aka C++ Builder) under Windows. The makefile
+is src/ptypes.mak.
+
MD5 (message digest) algorithm is implemented as an output stream class outmd5.
MacOS X/Intel, PPC: CC - Apple Objective-C compiler
+
SunOS/Sparc, i386: GNU C/C++
+
FreeBSD: GNU C/C++
+
HP-UX: aCC
+
Windows: MS Visual Studio 7, 8
+
+
+The build process on all platforms produces 3 versions of the library: static
+single-threaded, static multithreaded and dynamic (shared) multithreaded. In addition,
+MSVC can build debug versions for each of these 3 libraries.
+
Single-threaded versions have a suffix 'n' (non-reentrant) in their names:
+libptypesn.a for UNIX and MinGW and ptypesn.lib
+for MSVC. They are somewhat faster and smaller than the multithreaded versions
+and are intended to be used in smaller projects that do not require to be linked
+with the slower reentrant system libraries.
+
The dynamic versions of the library have the library version number in their
+names: ptypes21.dll, libptypes.so.21
+and libptypes.21.dylib. The version number in file names
+reflects incompatible changes and/or significant enhancement of functionality.
+Change in the third number in the version usually indicates a compatible improvement
+and/or bug fix release, so it is not reflected in the DLL/so file name.
+
+
+Building on UNIX and MacOS X
+
+
In order to build the library on one of the UNIX systems listed above, run
+make in the library's root directory. The makefile in
+this directory actually calls makefiles in src/ and
+wshare/ with a suffix which is an output of uname
+on the given system (e.g. src/Makefile.Linux or src/Makefile.FreeBSD).
+Make builds the library and the demo program, and then places:
+
+
Static single-threaded version -> lib/libptypesn.a
+
Static multi-threaded version -> lib/libptypes.a
+
Shared multi-threaded version -> so/libptypes.so.21
+(libptypes.21.dylib on MacOS X)
+
Demo program -> bin/wshare
+
+
The public headers are in include/.
+
When building your own multithreaded application on Linux or FreeBSD,
+GCC requires you to specify a special command-line option -pthread
+which automatically links POSIX threads library and the reentrant version of libc.
+On Linux you should specify a macro -D_GNU_SOURCE
+in the command line to include the rwlock interface.
+
When building your multithreaded application on SunOS, you should specify
+a macro -D_REENTRANT and also link the following libraries:
+-lpthread -lposix4 for multithreaded applications, and
+in addition, -lsocket -lnsl for network applications.
+NOTE: if you omit -lpthread, the program links
+without errors, but then the thread objects fail to initialize.
win32\PTypes_Lib.vcproj - static multithreaded version
+
win32\PTypes_DLL.vcproj - dynamic multithreaded version
+
win32\wshare.vcproj - the demo program
+
+
A "solution" file PTypes.sln is provided for building
+all components and versions of the library.
+
You can include one of the project files in your own solutions. Make your
+project dependent of PTypes to automatically link the library to your program.
+To use PTypes headers you will have to explicitly specify the directory in your
+project settings, e.g. "..\ptypes\include".
+
In order to link a program against the DLL version of PTypes (ptypes21.dll)
+use PTYPES_DLL macro definition when compiling your
+modules. You may want to add a post-build command in the MSVC environment that
+copies the PTypes DLL to the directory where you build and debug your application.
+
You should link your application with the multithreaded version of CRTL, except
+when using the single-threaded ptypesn.lib. When compiling
+with the dynamic version of PTypes, it is recommended also to use the multithreaded
+DLL version of CRTL.
+
Specify an additional library ws2_32.lib if you are
+using PTypes' IP socket classes.
+
+
+PTypes namespace
+
+
The entire PTypes interface is enclosed within a namespace called pt.
+The header file <pport.h> provides a
+macro USING_PTYPES, which is equivalent to using
+namespace pt. This macro allows you to use PTypes interface symbols without
+the scope specifier pt:: in your source code.
+
+
+Porting the library to other platforms
+
+
The author would greatly appreciate any effort to port the library to other
+popular platforms. If you either ported the library or just found that PTypes
+builds with no problem under your favorite platform with your favorite compiler,
+then all you'd have to do is to create a makefile with proper definitions in it.
+Take a look at Makefile.FreeBSD or Makefile.SunOS,
+for example. Besides OS_CXXOPTS you can specify additional
+libraries through OS_LDLIBS. Name your makefile so that
+running make Makefile.`uname` would work on the given
+operating system. Try to build the library and then run src/ptypes_test
+to make sure the library is functioning properly.
+
And finally, if you send the changes to the author, then (obviously) others
+would be able to benefit from using PTypes on your favorite operating system with
+your favorite compiler.
Some of the components in PTypes may produce unrecoverable error conditions,
+such like a list index is out of bounds, a stream object is constructed with inconsistent
+parameters, an operation on a stream is requested which requires buffering, etc.
+These errors are mainly caused by wrong usage of objects or functions of the library.
+
Whenever an unrecoverable error condition is raised, PTypes calls fatal()
+with a message describing the error condition and terminates the program. On most
+platforms this message is sent to stderr, and
+on Windows the message is shown in a simple message box.
Some library components also raise recoverable error conditions (exceptions).
+PTypes only generates exception of type (exception*)
+defined in <ptypes.h>, or, in some cases,
+a derivative class, e.g. (estream*). An exception object
+at least contains an error message which can be retrieved through get_message()
+method.
+
NOTE: Exception objects in PTypes are allocated dynamically and therefore,
+they should be freed by the catch block if it does not pass the exception farther.
+The exception class is derived from unknown to help
+you to detect memory leaks in your program. If a thread in a multithreaded application
+is created using PTypes thread class, then all PTypes
+exceptions thrown in each thread are caught and freed automatically (unless
+caught in the user code).
cset::cset() -- default constructor, initializes
+the set object to an empty set.
+
cset::cset(const cset& s) -- copy constructor.
+
cset::cset(const char* setinit) constructs a character
+set from a string. The setinit parameter is a sequence
+of characters, range specifiers and escape sequences. Range specifier consists
+of: lower boundary, dash "-" and higher boundary, e.g. "A-Z".
+Escape sequence begins with tilde "~" and can be followed by a two-digit
+hexadecimal number. Escape sequences are also used to include special characters
+tilde "~" and dash "-" (see examples below).
+
Constructing character sets using this interpreter can be a time-consuming
+operation. A better practice is to declare all constant character sets as static
+variables, so that the interpretation of all set constructing strings will be
+done only once during program startup.
+
An initializer string can also be passed to a cset
+object through assign().
+
Note: this constructor does not generate errors if the syntax is violated.
+
Examples:
+
+
cset s1 = "135A-CZ"; // digits 1, 3, 5, letters A through C, and also Z
+cset wspace1 = "~09~0d~0a "; // tab, CR, LF and space
+cset wspace2 = "~00-~20"; // all control and whitespace chars
+cset s2 = ":@~~"; // colon, at and tilde (must be escaped with another tilde)
+
The character set class (cset) implements Pascal-style
+set of integer values from 0 to 255, or set of characters. Unlike Pascal sets,
+the range of a cset cannot be changed and is always
+0 through 255. Cset class implements various operators
+(membership, union, intersection, equality, less than or equal to, etc.) as they
+are described in the Pascal language. See Operators
+for details.
+
Cset is a packed array of 256 bits, it occupies 32
+bytes of static or local memory. Each bit indicates whether the corresponding
+character is a member of a given set.
+
Another difference between cset and Pascal sets is
+that since C++ compiler does not have a built-in set constructor like the one
+in Pascal (e.g. ['A'..'Z', 'a'..'z']), cset provides
+a simple run-time interpreter instead (see Constructors).
The example below shows the general usage of character sets.
+
+
cset s = "A-Za-z!"; // same as ['A'..'Z', 'a'..'z', '!'] in Pascal
+
+include(s, '_'); // include underscore character
+include(s, '0', '9'); // include all chars from '0' to '9'
+
+if ('a' & s) // check membership
+ cout << "Letter 'a' found in the set! :)\n";
+
+const cset letters = "A-Za-z_"; // define a set of letters
+string tok = pin.token(letters); // read a token from the input stream
An ordinal O is in X + Y if and only if O is in X or Y (or both). Equivalent
+of bitwise OR.
+
+
+
O is in X - Y if and only if O is in X but not in Y. Equivalent of bitwise
+AND NOT.
+
+
+
O is in X * Y if and only if O is in both X and Y. Equivalent of bitwise AND.
+
+
+
The following rules apply to comparison operations <=, >=, ==, !=:
+
+
+
X <= Y is true just in case every member of X is a member of Y; Z >= W is
+equivalent to W <= Z.
+
+
+
U == V is true just in case U and V contain exactly the same members; otherwise,
+U != V is true.
+
+
+
For an ordinal O and a set S, O & S is true just in case O is a member
+of S. Unlike the Pascal language, where membership operator is in, PTypes
+uses ampersand "&" as a membership test operator.
+
Note: regardless of whether default char is signed or unsigned (usually
+set through compiler options) cset always treats char
+arguments as unsigned. This means, if the value of an argument is -1, e.g. in
+call to operator & or operator
++, the value will be converted to 255, -2 will be treated as 254, etc.
PTypes builds two separate versions of the library: static and shared (DLL
+on Windows), giving you a choice, and at the same time putting into a dilemma.
+
Both static and dynamic linking have their advantages and disadvantages. While
+small applications consisting of a single executable would prefer to link the
+library directly, more complex projects with multiple dynamic components and executables
+would greatly benefit from going 'totally dynamic', including generic low-level
+libraries, such like CRTL and PTypes. Before making a decision, consider the following:
+
Advantages of static linking in general:
+
+
Faster run-time loading (in return for slower compilation).
+
The installation process is simpler: the end-user doesn't have to deal with
+package dependencies, DLL/so versioning conflicts with other applications, etc.
+A simple application may run even without any special installation procedures.
+
Windows-specific: turning a static library into a DLL on Windows is not as
+simple as on UNIX systems. You should at least put special compiler directives
+against every public symbol in the library and thus make your header files less
+readable.
+
UNIX-specific: for security reasons, UNIX has strict policies with regard
+to finding shared libraries and managing allowed source paths. In most cases you
+will have to provide an automated installation procedure and require root privileges
+in order to place all shared libraries in the right place. Remember, UNIX is a
+true multiuser system, and often the user that downloaded/purchased your program
+does not have root access on his computer, and he doesn't want to bother the system
+administrator either. You may, of course, override LD_LIBRARY_PATH, but then shared
+libraries won't be truly shared if they are installed somewhere under user's home
+directory.
+
+
Advantages of dynamic linking in general:
+
+
Faster compilation and better modularization in big projects with distributed
+development (in return for slower run-time loading).
+
Bug fixes and improvements in library's implementation do not affect your
+application and do not require you to recompile everything. What's more, adding
+new features without changing the existing interfaces in the dynamic library,
+again, won't affect the other modules.
+
Smaller executables; dynamic libraries may be shared not only between the
+components of your project, but even between applications from different vendors.
+(The installation package should offer all dependent libraries anyway.) Freeware
+libraries perfectly suit for sharing between vendors, since they usually don't
+put any limitation on their usage, at least when linked dynamically.
+
Dynamic libraries can be loaded and bound to the main application at run-time
+allowing you to maintain functional 'drivers' (e.g. an application that extracts
+plain text from various text formats can support a generic text extraction interface;
+drivers for each format can be developed and shipped independently).
+
+
In summary, dynamic linking is good (1) for big projects, (2) if the library
+is widely used by many software vendors in its dynamic (shared) form or (3) to
+take advantage of run-time binding.
+
+Using and deploying UNIX shared object
+
PTypes builds a shared object named libptypes.so.20
+(libptypes.20.dylib on MacOS X), creates a 'default'
+symbolic link to this library and finally places them both in so/.
+
If you decided to link your program against PTypes shared object instead of
+the static library, then all you'd have to do is to change the library path in
+your makefile from -L../ptypes/lib to -L../ptypes/so.
+When running the program, it will require the shared library to be either in one
+of the default locations (usually /usr/lib and /usr/local/lib),
+or you will have to override the LD_LIBRARY_PATH environment variable and point
+to the directory where the shared object resides, e.g. ~/ptypes/so.
+
The shared object libptypes.so.20 should be deployed
+and installed along with your application. The version number '20' will change
+when PTypes adds a number of new features and becomes bigger and/or if incompatible
+changes take place which make it impossible for older programs to use the new
+shared object.
+
+Using and deploying Windows DLL
+
The PTypes MSVC project is configured so that the 'release' version of ptypes20.dll
+along with its import library is copied into so\ as
+the final step of the build process. This module does not contain any debugging
+information and is ready to be deployed. Note that the library itself is linked
+against the multithreaded DLL version of CRTL.
+
PTypes places a VERSION resource in the DLL, allowing the system or the setup
+program to automatically compare the existing ptypes20.dll
+the user may have in the system with the one that comes with your program. This
+is usually done from within the installation script.
+
The DLL version of the library built with Dev-C++/MinGW is called ptypes20g.dll.
+The reason this name is different is that each C++ compiler uses its own `name
+mangling' scheme for exported symbols, so that applications built by one compiler
+can't use dynamic libraries built by another one.
+
+Version checking sample code
+
If, for some reason, you wish to check the version of the shared library you
+linked with dynamically, you may check the global variable __ptypes_version,
+which is declared in <pport.h>.
+This variable holds the version number encoded as a single integer, e.g. 0x010902
+designates version 1.9.2. In this form the version numbers (required and actual)
+can be easily compared as integers.
+
Note, the name of the library itself reflects major and minor version numbers,
+so that only the third component of the version number can vary in the file.
+
If you need to check the version of PTypes before loading the library (for
+example, during the installation on UNIX), you may write a program that loads
+the shared object and reads the global symbol __ptypes_version
+at run-time, using the system dynamic loading interface. Note that this program
+itself is not using PTypes. Some UNIX systems require to link the program with
+-ldl.
Deploying the shared (dynamic) library
+Static vs. dynamic linking, deploying PTypes with your application, checking the
+version of the dynamic library.
Example 2 . This example demonstrates the basic usage of streaming network
+interfaces ipstream and ipstmserver.
+It consists of two programs: test client and test server. The
+server handles requests that contain a single word "Hello" by sending
+a response greeting back to the client. This example would work for named pipes
+as well, i.e. if you replace the class names ipstream
+and ipstmserver with namedpipe
+and npserver and fix the construction parameters.
+
Client:
+
+
+#include <pinet.h>
+
+USING_PTYPES
+
+const int testport = 8085;
+const int maxtoken = 4096;
+
+int main()
+{
+ // create a client socket and send a greeting to the server
+ // assuming that the server is on the same host (127.0.0.1)
+
+ ipstream client(ipaddress(127, 0, 0, 1), testport);
+
+ try
+ {
+ client.open();
+
+ pout.put("Sending a request to the server...\n");
+ client.putline("Hello");
+ client.flush();
+
+ // receive the response
+ string rsp = client.line(maxtoken);
+ pout.putf("Received: %s\n", pconst(rsp));
+
+ // need to close the socket explicitly to gracefully shutdown
+ // the peer host too. otherwise, ~ipstream() will call cancel()
+ // and leave the peer in a waiting state (not forever though).
+ client.close();
+ }
+ catch(estream* e)
+ {
+ perr.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ return 0;
+}
+
+
+
Server:
+
+
+#include <ptime.h>
+#include <pinet.h>
+
+USING_PTYPES
+
+const int testport = 8085;
+const int maxtoken = 4096;
+
+void servermain(ipstmserver& svr)
+{
+ ipstream client;
+
+ pout.putf("Ready to answer queries on port %d\n", testport);
+
+ while(true)
+ {
+ // serve() will wait for a connection request and will prepare
+ // the supplied ipstream object for talking to the peer.
+ // note that (unlikely) exceptions thrown in serve() will be
+ // caught in main()
+ svr.serve(client);
+
+ // for better performance the server would start a new thread
+ // for each client request. for simplicity, we serve the request
+ // in-place. see multithreading examples for the full multithreaded
+ // server template.
+ if (client.get_active())
+ {
+ try
+ {
+ // read the request line:
+ // real-world network applications should limit input data
+ // to prevent potential denial-of-service attacks
+ string req = lowercase(client.line(maxtoken));
+ if (req == "hello")
+ {
+ // try to reverse-lookup the client's IP
+ string host = phostbyaddr(client.get_ip());
+ if (isempty(host))
+ host = iptostring(client.get_ip());
+
+ // now send our greeting to the client
+ client.putline("Hello, " + host + " ("
+ + iptostring(client.get_ip()) + "), nice to see you!");
+ client.flush();
+
+ // log this request
+ pout.putf("%t greeting received from %s (%a)\n",
+ now(), pconst(host), long(client.get_ip()));
+ }
+
+ client.close();
+ }
+ catch(estream* e)
+ {
+ perr.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+ }
+ }
+}
+
+int main()
+{
+ ipstmserver svr;
+
+ try
+ {
+ // bind to all local addresses on port 8085
+ svr.bindall(testport);
+
+ // enter an infinite loop of serving requests
+ servermain(svr);
+ }
+ catch(estream* e)
+ {
+ perr.putf("FATAL: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ return 0;
+}
+
+
+
Example 3 . This example demonstrates the use of message-oriented networking
+interfaces ipmessage and ipmsgserver.
+The client sends a broadcast message to the local network and waits for a response.
+It may retry the request several times.
+
Client:
+
+
+#include <pinet.h>
+
+USING_PTYPES
+
+const int testport = 8085;
+const int maxtoken = 4096;
+
+const int tries = 3;
+const int firsttimeout = 2000;
+
+
+bool dorequest(int timeout)
+{
+ ipmessage msg(ipbcast, testport);
+
+ try
+ {
+ pout.put("Broadcasting a request...\n");
+ msg.send("Hello");
+
+ // wait for a response the specified amount of time
+ if (!msg.waitfor(timeout))
+ return false;
+
+ ipaddress src;
+ string rsp = msg.receive(maxtoken, src);
+ pout.putf("Received: '%s' (from %a)\n", pconst(rsp), long(src));
+ }
+ catch(estream* e)
+ {
+ perr.putf("Error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ return true;
+}
+
+
+int main()
+{
+ int timeout = firsttimeout;
+ for (int i = 0; i < tries; i++)
+ {
+ if (dorequest(timeout))
+ break;
+ // double the timeout value
+ timeout *= 2;
+ }
+
+ return 0;
+}
+
+
+
+
Server:
+
+
+#include <ptime.h>
+#include <pinet.h>
+
+USING_PTYPES
+
+const int testport = 8085;
+const int maxtoken = 4096;
+
+
+void servermain(ipmsgserver& svr)
+{
+ pout.putf("Ready to answer queries on port %d\n", testport);
+
+ bool quit = false;
+ do
+ {
+ try
+ {
+ // receive the "hello" request and send a simple answer
+ // back to the client
+ string req = lowercase(svr.receive(maxtoken));
+ if (req == "hello")
+ {
+ string host = svr.get_host();
+ if (isempty(host))
+ host = iptostring(svr.get_ip());
+
+ svr.send("Hello, " + host + " ("
+ + iptostring(svr.get_ip()) + "), nice to see you!");
+
+ // log this request
+ pout.putf("%t greeting received from %s (%a)\n",
+ now(), pconst(host), long(svr.get_ip()));
+ }
+ }
+ catch(estream* e)
+ {
+ perr.putf("Server error: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+ } while (!quit);
+}
+
+
+int main()
+{
+ ipmsgserver svr;
+
+ try
+ {
+ svr.bindall(testport);
+
+ // try to listen on socket once to generate an error right away,
+ // before entering the main server loop
+ svr.poll();
+
+ // enter an infinite loop of serving requests
+ servermain(svr);
+ }
+ catch(estream* e)
+ {
+ perr.putf("FATAL: %s\n", pconst(e->get_message()));
+ delete e;
+ }
+
+
+ return 0;
+}
+
PTypes' networking module provides simple means of creating both client and
+server applications communicating over a IP network. The networking classes and
+utilities are built on top of BSD sockets and WinSock2 frameworks. PTypes not
+only solves and hides all incompatibility issues between these two API's, but
+also makes the task of creating networking applications even simpler.
+
Building client/server applications requires you to either use an existing
+protocol (such like HTTP, FTP, SMTP) or develop your own high-level communication
+protocol for your specific needs. Since the latter may be a nontrivial task, to
+avoid logical mistakes in the communication process we recommend you, first of
+all, to study some of the widely used protocols, and second, use one of the existing
+protocols with possible modifications instead of creating new ones. For example,
+HTTP (Hypertext Transfer Protocol) used by web servers and browsers was designed
+to be as generic as possible, so that it can be used virtually for any task with
+simple request-response logic.
+
A real-world example of using PTypes networking is included in the source code
+(See wshare).
+
The networking classes and utilities are declared in <pinet.h>.
#include <pinet.h>
+
+class ipmessage {
+ ipmessage();
+ ipmessage(ipaddress ip, int port);
+ ipmessage(string host, int port);
+
+ bool waitfor(int timeout);
+ void send(const char* buf, int count);
+ void send(string s); int receive(char* buf, int count [, ipaddress& src ] );
+ string receive(int max [, ipaddress& src ] );
+
+ ipaddress get/set_ip();
+ string get/set_host();
+ int get/set_port();
+ ipaddress get_myip();
+ int get_myport();
+
+ virtual void sockopt(int socket);
+}
+
+
The ipmessage class implements connectionless, unreliable
+datagram communication between IP hosts. The underlying protocol (UDP) never guarantees
+that a message will be delivered, however, in return, it has
+the ability to send broadcast messages on a local network. Unlike stream-oriented
+protocols which have some traffic overhead because of the control packets (for
+opening/closing connections and for confirming each delivery), message-oriented
+protocols always send a single packet, sometimes fragmented if it exceeds the
+size of a physical frame.
+
In summary, message-oriented communication is useful in the following situations:
+
+
For sending streaming data (as a rule, sound or video) when losing packets
+is not crucial. An application may measure the bandwidth and the reliability of
+a connection before starting a streaming session, to estimate the optimal frequency
+of packets and adjust the quality of multimedia data (e.g. frames per second,
+resolution, etc).
+
For finding hosts of a specific type on a local network using broadcast/multicast
+messages. You may want your client application to find its services on a network
+automatically to free the user from entering the addresses manually.
+
For sending very short messages or request/reply cycles, possibly with confirmation/retry
+mechanism. In most cases developing such applications may be costly compared to
+using stream-oriented protocols instead.
+
+
The maximum message size is limited to 64 KBytes on most systems. Note however,
+that sending large messages may result in fragmentation and hence a lesser probability
+that the whole message will be delivered. You may assume that a maximum data size
+for a UDP message is 1472 bytes, even though such message may still be fragmented
+when transferred over a non-Ethernet medium. The size of a guaranteed indivisible
+UDP packet is 512 bytes on all physical media types.
+
For larger data chunks you may consider using streaming protocols, since the
+TCP control traffic overhead is insignificant compared to data in such cases.
+
The ipmessage and ipmsgserver
+classes are not compatible with PTypes streaming interfaces due to unreliable
+and connectionless nature of the underlying protocol. These classes provide a
+pair of low-level methods receive() and send()
+and require that the client (ipmessage) first call send()
+prior to receiving, and the server (ipmsgserver) must
+first receive data prior to sending. In addition, the server object can be polled
+for pending data (optionally with timed waiting) using poll().
+
The ipmessage class is reusable, i.e. you may use
+one object to send data to multiple destinations by changing the ip
+(or host) and port properties.
+
Ipmessage can generate exceptions of type (estream*)
+with a corresponding error code and a message string.
ipmessage::ipmessage() is the default constructor.
+
ipmessage::ipmessage(ipaddress ip, int port) constructs
+an ipmessage object and assigns the peer ip/port
+values. To send a broadcast message to all hosts on a local network, assign a
+predefined constant ipbcast to ip.
+
ipmessage::ipmessage(string host, int port) constructs
+an ipmessage object and assigns the peer host name and
+port values. Before actually sending data first time, the object resolves the
+host name to a numeric IP address. Host can be either
+a symbolic DNS name or a numeric address in a string form (e.g. "somehost.com"
+or "192.168.1.1").
+
bool ipmessage::waitfor(int milliseconds) waits on
+a socket until data is available for reading (returns true)
+or the time specified has elapsed, in which case it returns false.
+
ipmessage::send(const char* buf, int count) sends
+data to the peer. Ip/host
+and port properties must be assigned prior to calling
+send(). A client must first call send()
+before receiving data from the peer.
+
ipmessage::send(string s) works like the previous
+version of send() except that it sends the string s
+(not including the terminating null-symbol).
+
int ipmessage::receive(char* buf, int count [, ipaddress&
+src ] ) reads data from the socket. Receive()
+may hang if no data is available for reading. This function returns the actual
+number of bytes read. If the packet received exceeds the size of the supplied
+buffer, an exception is raised with code EMSGSIZE. You may check if there is data
+available for reading without 'hanging' using waitfor()
+described above. The last optional parameter src receives
+the IP address of the host that sent this packet: it may be useful if the packet
+is received in response to a broadcast request.
+
string ipmessage::receive(int max [, ipaddress& src ]
+) works like the previous version of receive()
+except that it returns data in a dynamic string. The parameter max
+specifies the limit which may not be exceeded when reading data from the network,
+like with the previous version of receive().
+
ipaddress ipmessage::get/set_ip() sets/retrieves the
+peer address in a numerical form. If the object was constructed using a symbolic
+name, get_ip() may perform a DNS lookup (only once).
+To send a broadcast message to all hosts on a local network, assign a predefined
+constant ipbcast to this property.
+
string ipmessage::get/set_host() sets/retrieves the
+peer address in a symbolic form. If the object was constructed using a numeric
+IP address, get_host() may perform a reverse DNS lookup.
+
int ipmessage::get/set_port() sets/retrieves the peer
+port number.
+
ipaddress ipmessage::get_myip() returns the local
+address associated with the socket.
+
int ipmessage::get_myport() returns the local port
+number associated with the socket.
+
virtual void ipmessage::sockopt(int socket) - override this method in a descendant class if you want to set up additional socket options (normally, by calling setsockopt()).
#include <pinet.h>
+
+class ipmsgserver {
+ ipmsgserver();
+
+ int bind(ipaddress ip, int port);
+ int bindall(int port);
+
+ bool poll(int bindnum = -1, int timeout = 0);
+ int receive(char* buf, int count);
+ string receive(int max);
+ void send(const char* buf, int count);
+ void send(string s);
+ void sendto(const char* buf, int count, ipaddress ip, int port);
+ void sendto(string s, ipaddress ip, int port)
+
+ ipaddress get_ip();
+ string get_host();
+
+ virtual void sockopt(int socket);
+}
+
+
+
The ipmsgserver class is used on the server side
+of a client-server application. It bounds itself to a specified port/address and
+waits until a packet is received from a client host. Once a packet is read with
+receive(), subsequent calls to send()
+will post data back to the client that sent the last request. Each request must
+be fulfilled immediately; unlike the stream-oriented server class, ipmsgserver
+can not handle requests concurrently.
+
ipmsgserver can generate exceptions of type (estream*)
+with a corresponding error code and a message string.
ipmsgserver::ipmsgserver() constructs an ipmsgserver
+object.
+
int ipmsgserver::bind(ipaddress ip, int port) binds
+the server to the specified local IP address and port number. This function can
+be called multiple times for different local addresses and port numbers. Bind()
+returns a value that can be used later in call to poll()
+as the parameter bindnum.
+
int ipmsgserver::bindall(int port) binds the server
+to all local IP addresses on the specified port number. Can be called multiple
+times for different port numbers. Bindall() returns
+a value that can be used later in call to poll() as
+the parameter bindnum.
+
bool ipmsgserver::poll(int bindnum = -1, int timeout = 0)
+polls the listening sockets for data available for reading. Bindnum
+specifies the socket number reutrned by bind() or bindall().
+If this parameter is -1 poll() tests all sockets. The
+second parameter timeout specifies the amount of time
+in milliseconds to wait for data. If timeout is 0 poll()
+returns immediately; if it's -1 poll() waits infinitely.
+This function returns true if there is data available
+for reading.
+
int ipmsgserver::receive(char* buf, int count) reads
+data from the socket. Receive() may hang if no data
+is available for reading. This function returns the actual number of bytes read.
+If the packet received exceeds the size of the supplied buffer, an exception is
+raised with code EMSGSIZE. You may check if there is data available for reading
+without 'hanging' using poll() described above.
+
string ipmsgserver::receive(int max) works like the
+previous version of receive() except that it returns
+data in a dynamic string. The parameter max specifies
+the limit which may not be exceeded when reading data from the network, like with
+the previous version of receive().
+
void ipmsgserver::send(const char* buf, int count)
+sends data to the peer. The destination address is determined from the last packet
+read using receive().
+
void ipmsgserver::send(string s) works like the previous
+version of send() except that it sends the string s
+(not including the terminating null-symbol).
+
void ipmsgserver::sendto(const char* buf, int count, ipaddress ip, int port)
+sends data to the specified address and port.
+
void ipmsgserver::sendto(string s, ipaddress ip, int port) works like the previous
+version of send() except that it sends the string s
+(not including the terminating null-symbol).
+
ipaddress ipmsgserver::get_ip() returns the IP address
+of the peer. The information about the peer is determined during a successful
+call to receive().
+
string ipmsgserver::get_host() returns the peer host
+name. A reverse DNS lookup may be performed if necessary. The information about
+the peer is determined during a successful call to receive().
+
virtual void ipmsgserver::sockopt(int socket) - override this method in a descendant class if you want to set up additional socket options (normally, by calling setsockopt()).
#include <pinet.h>
+
+class ipstmserver {
+ ipstmserver();
+
+ int bind(ipaddress ip, int port);
+ int bindall(int port);
+
+ bool poll(int bindnum = -1, int timeout = 0);
+ bool serve(ipstream& client, int bindnum = -1, int timeout = -1);
+
+ virtual void sockopt(int socket);
+}
+
+
The ipstmserver class is used on the server side
+of a stream-oriented client-server application. It bounds itself to a specified
+port/address and waits until a connection request is received from a client host.
+For each connection request a server application performs some actions and returns
+to the waiting state. For better performance your daemon may start a new thread
+for each client connection.
+
Ipstmserver can generate exceptions of type (estream*)
+with a corresponding error code and a message string.
+
ipstmserver::ipstmserver() constructs an ipstmserver
+object.
+
int ipstmserver::bind(ipaddress ip, int port) binds
+the server to the specified local IP address and port number. This function can
+be called multiple times for different local addresses and port numbers. Bind()
+returns a value that can be used later in call to poll()
+and serve() as the parameter bindnum.
+
int ipstmserver::bindall(int port) binds the server
+to all local IP addresses on the specified port number. Can be called multiple
+times for different port numbers. Bindall() returns
+a value that can be used later in call to poll() and
+serve() as the parameter bindnum.
+
bool ipstmserver::poll(int bindnum = -1, int timeout = 0)
+polls the listening sockets for connection requests. Bindnum
+specifies the socket number reutrned by bind() or bindall().
+If bindnum is -1 poll() tests
+all sockets. The second parameter timeout specifies
+the amount of time in milliseconds to wait for a connection request. If timeout
+is 0 poll() returns immediately; if it's -1 poll()
+waits infinitely. This function returns true if there
+is a new connection request waiting for processing.
+
bool ipstmserver::serve(ipstream& client, int bindnum
+= -1, int timeout = -1) polls the specified bound sockets for connection
+requests. If there is a connection request, serve()
+opens and prepares the supplied ipstream object for
+communicating with the client, i.e. client will be active
+upon return from serve() and will contain the peer IP
+address and the port number. The meanings of bindnum
+and timeout are the same as for poll()
+except that the default value for timeout in this case
+is -1, i.e. wait infinitely. This function returns true
+if there is a new connection request and client is active,
+or false if the call has timed out.
+
virtual void ipstmserver::sockopt(int socket) - override this method in a descendant class if you want to set up additional socket options (normally, by calling setsockopt()).
The ipstream class implements full-duplex streaming
+communication between IP hosts. Ipstream incorporates
+iobase, instm
+and outstm interfaces and adds two more properties:
+peer IP address and peer port number. This means, you can work with ipstream
+the same way you work with infile and outfile,
+except that you specify the target IP address or the symbolic hostname along with
+the port number instead of the filename when constructing an object. Besides,
+the rules of object-oriented inheritance allow you to manipulate ipstream
+objects in those parts of your code which are only 'familiar' with the basic instm
+and outstm interfaces.
+
In order to open a connection and exchange data you need to set target (peer
+host) address and the port number. The peer address can be either a symbolic DNS
+name or a numeric IPv4 address. Regardless of which one of these two properties
+you set first, the ipstream object can perform DNS resolving
+as necessary and return both properties on your request.
+
Ipstream adds the following status codes: IO_RESOLVING,
+IO_RESOLVED, IO_CONNECTING, IO_CONNECTED (see also iobase
+for the status codes common to all streams).
+
On UNIX the library startup code blocks SIGPIPE,
+so that all error conditions on sockets always raise exceptions of type (estream*).
+
ipstream::ipstream() is the default constructor.
+
ipstream::ipstream(ipaddress ip, int port) constructs
+an ipstream object and assigns the peer ip/port
+values.
+
ipstream::ipstream(string host, int port) constructs
+an ipstream object and assigns the peer host name and
+port values. Before actually opening the stream ipstream
+resolves the host name to a numeric IP address. Host
+can be either a symbolic DNS name or even a numeric address in a string form (e.g.
+"somehost.com" or "192.168.1.1").
+
ipstream::~ipstream() cancels the connection immediately
+and destroys the object. To shut down the connection 'gracefully' you should call
+close() before destroying the object or before the program
+goes beyond the scope of a static/local ipstream object.
+
void ipstream::open() opens a connection with the
+peer. host or ip along with
+port properties must be set before opening the stream.
+
void ipstream::close() closes the stream 'gracefully',
+and in some situations may be time-consuming. Although all stream objects in PTypes
+are closed by the destructors automatically, it is recommended to explicitly
+call close() for socket objects.
+
void ipstream::cancel() closes the stream immediately.
+If the connection was successful, calling cancel() may
+leave the peer host in a waiting state for a long time.
+
bool ipstream::waitfor(int milliseconds) waits on
+the socket until either data is available for reading (returns true)
+or the time specified in the parameter has elapsed, in which case it returns false.
+
ipaddress ipstream::get/set_ip() sets/retrieves the
+peer address in a numerical form. If the object was constructed using a symbolic
+name, get_ip() may perform a DNS lookup (only once).
+
string ipstream::get/set_host() sets/retrieves the
+peer address in a symbolic form. If the object was constructed using a numeric
+IP address, get_host() may perform a reverse DNS lookup.
+
int ipstream::get/set_port() sets/retrieves the peer
+port number.
+
ipaddress ipstream::get_myip() returns the local address
+associated with the socket.
+
int ipstream::get_myport() returns the local port
+number associated with the socket.
+
virtual void ipstream::sockopt(int socket) - override this method in a descendant class if you want to set up additional socket options (normally, by calling setsockopt()).
Many PTypes networking utility functions and class methods manipulate a new
+data type ipaddress - IPv4 (4-byte) internetwork address
+type. The objects of this type can be constructed either by explicitly specifying
+the 4 bytes separately (e.g. ipaddress(192, 168, 1, 1))
+or by assigning another ipaddress. The objects of this
+type are mutually compatible with unsigned long type,
+however, you can not rely on the order of bytes if an ipaddress
+is converted to a unsigned long and vice versa. This
+implicit typecast is provided only for comparing an ipaddress
+value with 0 or ipnone (see Examples).
+
string iptostring(ipaddress ip) converts an IP address
+to a string, e.g. ipaddress(127, 0, 0, 1) would be "127.0.0.1".
+
ipaddress phostbyname(const string& name) resolves
+a symbolic DNS name or a numeric IP addresses to ipaddress.
+On error this function returns ipnone.
+
string phostbyaddr(ipaddress ip) performs reverse
+DNS lookup for the given IP address. On error this function returns an empty string.
+
string phostcname(const string& name) returns
+the canonical name of the host. On error this function returns an empty string.
Threads and synchronization primitives solve the vital problem of diversity
+of the threading API's on different platforms. The library also offers message
+queues and job queues as additional methods of thread synchronization and maintenance.
+
+
+
IP socket classes and utilities provide complete IP-based framework for
+ both client-side and server-side programming. Combined with PTypes multithreading,
+ these classes can be used for designing complex non-visual applications,
+ such like network daemons or web robots.
+
+
+
Dynamic strings, variants, character sets, date/time type and various
+ kinds of dynamic and associative arrays: Delphi programmers will find them
+ very similar to the ones in their favorite language. The collection of
+ these basic data types may be useful, among other things, for building
+ compilers and interpreters for higher-level languages.
+
+
+
Streaming interfaces provide buffered I/O with simple and powerful text
+ parsing methods. A strictly defined syntax for a given text format or a
+ formal language can be represented by calls to PTypes token extraction
+ methods. The unified streaming interface is applicable to files, named
+ pipes and network sockets.
+
+
+
+
Special thread class with enhanced functionality called unit. Units have their
+own main() and input/output 'plugs'; they can be connected to each other within
+one application to form pipes, like processes in the UNIX shell.
+
+
+
Finally, everything above is portable: all platform-dependent details
+ are hidden inside.
+
+
+
+ Why use PTypes?
+
+
PTypes is yet another generic class library among tens and hundreds of others.
+ To find its own niche in this variety of generic programming tools we decided
+ to emphasis on simplicity of the interfaces and readability of the resulting
+ code (i.e. the code that uses the library). The library focuses
+ only on most widely used data types and functional interfaces. We think that
+ like programming languages themselves, low-level libraries, too, should use
+ the minimal set of notions for covering most programming patterns.
+
+
Some data types in the library (strings, character sets and variants) use algebraic
+notation rather than object-oriented, i.e. you write length(s)
+for a string object instead of s.length(). We believe
+that traditional notation better fits to a class that represents some fundamental
+notion and is not supposed to produce subclasses with overridden methods. In other
+words, if there is no inheritance and polymorphism, there is no need for method
+calls as well.
+
PTypes uses old-fashioned all-lowercase naming style, since it can
+ peacefully co-exist with other styles in one program. Whether you are a `unixoid'
+ or you are using modern writing styles (Hungarian, Borland, etc.) you can
+ combine your favorite style with PTypes with no or very little harm to your
+ source code.
+
And finally, in order to achieve better readability of the resulting
+ code we use
+ clean
+ class
+ naming,
+ i.e. the string type is called string, thread
+ is thread,
+ etc.
+
We designed this library in the hope that PTypes with its conciseness and
+ intuitiveness could find its own 'target audience'.
+
+
+ Versions and availability
+
+
+
Version 2.1 of PTypes is the eleventh public release. The third number in the
+version indicates insignificant improvements or bug fixes. You might want to take
+a look at the Changes page if you are familiar with
+one of the previous versions.
+
+
The latest source code and both on-line and out-of-line documentation can be
+found at
The development version of PTypes is available at SourceForge.net's CVS server.
+If you are willing to join the development of the library, please become a SourceForge.net
+user and then contact me to get full access to the CVS repository. The file TODO
+in the development branch contains the immediate development plan for the library.
+
+
+ Bugs and known problems
+
+
The list of problems encountered in the latest version along with their
+ solutions, if available, can be found at PTypes project management page (at
+ SourceForge.net):
Pierre-Frederic Caillaud <peufeu at free dot fr>
+Ralph Siemsen <ralphs at netwinder dot org>
+Marco Geri <m.geri at list dot it>
+Philippe Le Rohellec <plerohel at hotmail dot com>
+Mark Lentczner <markl at glyphic dot com>
+Jordan Ritter <jpr5 at darkridge dot com>
+Sergey Matveychuk <sem at ciam dot ru>
+
+
+
+ Contacts
+
+
PTypes is open and free, which also means it is open to your comments and
+ suggestions. Please, submit bug reports and feature requests through PTypes
+ project management page at sf.net. There is a public discussion
+ forum for general questions.
+
The author would be grateful if you let him know that you use the library
+ in your project. The author's email address is:
tobjlist - dynamic array of pointers to
+arbitrary objects
+
+
+
tstrlist - dynamic list and associative
+array of string/object pairs
+
+
+
textmap - associative array of textual key/value
+pairs
+
+
+
+PTypes offers a set of list templates/classes that were designed to eliminate
+the template 'code bloat' problem and also to allow reallocating of dynamic arrays
+in the most efficient way.
+
Why not STL containers? Or, why there is no universal container for
+arbitrary types, like std::vector, in PTypes? There are 3 main reasons for this:
+
+
A universal container template would require the item type to be either a
+POD-type or a copyable class, since it copies items during reallocation of the
+dynamic array. Otherwise, if its implementation would use fragmentation to avoid
+copying, the indexed access would become a costly operation. As a result, you
+are forced to provide copy constructors (often bulky, non-trivial code) for many
+classes in your program, even though it wouldn't be necessary otherwise.
+
+
+
Such container would be (and in real world it is) very inefficient for the
+same reason: each reallocation of the dynamic array requires items to be copied
+instead of just moved. Possibly a built-in reallocation operator in C++ would
+solve this problem.
+
+
+
The 'code bloat' problem: containers like std:vector usually produce enormous
+amount of automatically generated code (sometimes duplicate code) during instantiation
+of templates.
+
+
+Overview of PTypes list templates. As a replacement of a universal vector-like
+container PTypes offers two separate templates: tpodlist
+and tobjlist. Each of them have certain limitations
+in return for the following advantages: dynamic arrays are reallocated in the most
+efficient way, templates produce no or very little code during instantiation.
+
The tpodlist template is intended for use with small
+POD (plain-old-data) objects. For bigger structures or for arbitrary non-POD types
+in general the library offers a list of pointers, tobjlist,
+that has the ability to automatically destroy objects when they are removed from
+the list. Tobjlist never reallocates items themselves,
+instead, it only deals with pointers.
+
As a universal way of holding string/object pairs in an indexed list the library
+offers the tstrlist template. When constructed as a
+sorted list, this template can also serve as a map indexed by textual keys. And
+finally, the textmap class provides a simple interface
+for mapping strings to strings, i.e. textual keys to values.
+
Historically, PTypes was using list classes that required the list item to
+be derived from unknown. These list classes (objlist,
+strlist and strmap) are still present in the library for backward compatibility.
+There is no such limitation in the newer versions of the library, however, deriving
+your classes from unknown can still give you an advantage
+of automatically detecting memory leaks in your program.
+
Bounds checking. All list operations that involve indexes can be checked
+for validity at run-time. If an index value is invalid, the library generates
+an unrecoverable (fatal) error. Bounds checking is done when compiling your code
+in debug mode (i.e. if macro DEBUG is defined). You
+may also enable it explicitly for your release builds by defining CHECK_BOUNDS.
+Note that the bounds checking code is fully inlined and the library itself is
+not affected by these macros.
+The textmap class implements an associative array of
+strings, i.e. textual key/value pairs. Items in a textmap
+object can be accessed both via integral indexes and textual keys. Internally,
+textmap keeps the list sorted in alphabetical order
+and uses binary search to find keys in a list.
+
The methods get_count(), clear()
+and pack() work as for tobjlist
+and are not described in this section.
int textmap::put(string key, string value) associates
+value with key. This method
+can either insert or replace an existing value, as well as remove it from a textmap
+if value is an empty string.
+
string textmap::operator [](string key) returns a
+value associated with the key, or an empty string if
+key does not exist in a textmap.
+
string textmap::operator [](int index) returns a value
+at the position index. This method along with get_count()
+can be used to iterate through a textmap.
+
string textmap::getkey(int index) returns the key
+value at the position index.
+The tobjlist template implements a dynamic array of
+pointers to objects of an arbitrary type X. Since the
+list itself only holds pointers to objects, the element type can be any structure,
+including a class with constructors and destructors. The element type is not required
+to have a copy constructor. A list can contain objects of any derivative class
+of X as well.
+
Tobjlist can optionally (with ownobjects
+= true) be responsible for freeing objects whenever
+they are removed from a list, in which case the objects are required to be dynamically
+allocated with operator new. Objects can be automatically
+freed by the following methods: set_count() if the new
+value is less than the old one, also del(), clear(),
+put() and ~tobjlist().
+
tobjlist::tobjlist(bool ownobjects = false) constructs
+a tobjlist object. See note for the parameter ownobjects
+above.
+
tobjlist::~tobjlist() calls clear()
+and destroys the list object.
+
int tobjlist::get/set_count(int) gets or sets the
+number of items in a list. If the new value for count
+is greater than the old one, all new slots are filled with NULL pointers. If it's
+smaller and if ownobjects is true, extra objects are
+freed.
+
int tobjlist::get/set_capacity(int) gets or sets the
+capacity of a list. The capacity property reflects the
+number of slots actually allocated for a list and is set automatically by other
+methods whenever necessary. Like tpodlist, tobjlist
+uses a 'lazy allocation' technique (see also tpodlist::get/set_capacity).
+
bool tobjlist::get/set_ownobjects(bool) returns or
+sets the ownobjects flag.
+
void tobjlist::ins(int index, X* obj) inserts the
+object obj into a list at the position index.
+All pointers with greater indexes are moved up to make room for the new pointer.
+
void tobjlist::add(X* obj) adds the object obj
+to the end of a list.
+
X* tobjlist::operator [](int index) returns the pointer
+to an object at the position index.
+
X* tobjlist::top() returns the last pointer in a list.
+
void tobjlist::put(int index, X* obj) replaces the
+pointer at the position index with obj.
+Can free the old object if ownobjects is true.
+
void tobjlist::del(int index) deletes the pointer
+at the position index and moves all pointers with greater
+indexes down. Can also free the object if ownobjects
+is true.
+
X* tobjlist::pop() returns the last pointer in a list
+and deletes it from a list.
+
void tobjlist::clear() deletes all pointers from a
+list. Can also free all objects if ownobjects is true.
+
void tobjlist::pack() sets capacity
+equal to count. You can call pack()
+after deleting multiple pointers from a list to optimize memory usage.
+
bool tobjlist::search(const void* key, int& index)
+performs binary search on a list. The virtual method compare()
+(below) must be overridden in order for search() to
+work.
+
virtual int tobjlist::compare(const void* key, const void*
+obj) override this method in a descendant class to be able to perform binary
+search on a list. The value of key is the same as in
+the call to search(). Obj
+is a pointer to an object that must be compared against key.
+The return value must be -1, 0 or 1, similarly to the function strcmp().
+The tpodlist template implements a dynamic array of
+so-called POD (plain-old-data) objects. POD types in C++ are: all integral types,
+pointers, floating point types, and also compound types (i.e. arrays and structures)
+that contain only POD items. With optimizing compilation, the instantiation of
+this template produces no extra code.
+
The parameter X of this template specifies the element
+type for the list. The optional parameter initzero indicates
+whether the memory allocated for new elements should be initialized to zero in
+methods ins(int), add() and
+set_count(int).
+
tpodlist::tpodlist() is the default constructor.
+
tpodlist& tpodlist::operator =(const tpodlist& t)
+is an assignment operator that clears a list and then copies all items from t.
+
int tpodlist::get/set_count(int) gets or sets the
+number of items in a list.
+
int tpodlist::get/set_capacity(int) gets or sets the
+capacity of a list. The capacity property reflects the
+number of items actually allocated for a list and is set automatically by other
+methods whenever necessary. Tpodlist uses a 'lazy allocation'
+technique for better performance: when items are added with add()
+or ins(), the capacity grows in bigger increments. This
+property, however, does not change when you delete items from a list, with only
+exception when count becomes 0, in which case capacity
+is also set to 0. You can call pack() to optimize memory
+usage after deleting multiple items. Setting capacity
+to a value less than count is an error.
+
X& tpodlist::ins(int index) allocates a slot for
+a new item at the position index and returns a reference
+to the slot. All items with greater indexes are moved up to make room for the
+new item. The slot can be initialized to zero if the template parameter initzero
+was true. Calling ins() with
+index equal to count is equivalent
+to calling add().
+
void tpodlist::ins(int index, const X& item) inserts
+item at index.
+
void tpodlist::ins(int index, const tpodlist& t)
+inserts all items of the list t at the position index.
+
X& tpodlist::add() allocates a slot for a new
+item at the end of a list and returns a reference to the slot.
+
void tpodlist::add(const X& item) appends item
+to a list.
+
void tpodlist::add(const tpodlist& t) appends
+the list t to a given list.
+
X& tpodlist::operator [](int index) returns a
+reference to an item at the position index. A const
+version of this operator also exists.
+
X& tpodlist::top() returns a reference to the
+last item (one that has the greatest index) in a list.
+
void tpodlist::del(int index, int count = 1) deletes
+count items from a list and moves all items with greater
+indexes down. The parameter count is optional and defaults
+to 1.
+
void tpodlist::pop() deletes the last item from a
+list.
+
void tpodlist::clear() deletes all items from a list
+and also sets capacity to 0.
+
void tpodlist::pack() sets capacity
+equal to count. You can call pack()
+after deleting multiple items from a list to optimize memory usage.
template <class X> class tstrlist {
+ tstrlist(int flags = 0);
+ ~tstrlist();
+
+ int get/set_count(int);
+ int get/set_capacity(int);
+ bool get/set_ownobjects(bool);
+ void clear();
+ void pack();
+
+ bool get_sorted() const;
+ bool get_duplicates() const;
+ bool get_casesens() const;
+
+ // methods that work both on sorted and unsorted lists
+ void ins(int index, string key, X* obj);
+ void put(int index, string key, X* obj);
+ void put(int index, X* obj);
+ int add(string key, X* obj);
+ X* operator [](int index) const;
+ string getkey(int index) const;
+ void del(int index);
+ int indexof(string key) const;
+ int indexof(void* obj) const;
+
+ // these methods are allowed only on sorted lists
+ int put(string key, X* obj);
+ X* operator [](string key) const;
+ void del(string key);
+ bool search(string key, int& index) const;
+}
+
+
+The tstrlist template is similar to tobjlist
+in many ways, except that it maintains pairs of strings and objects of type X,
+and defines some additional methods described below. Tstrlist
+can optionally be sorted by string keys, which allows to use it as an associative
+array of objects. Thus, tstrlist combines functionality
+of an indexed dynamic array and an associative array at the same time. Like tobjlist,
+tstrlist can 'own objects', which means it can automatically
+free objects whenever they are removed from a list.
+
The methods get/set_count(), get/set_capacity(),
+get/set_ownobjects(), clear()
+and pack() work as for tobjlist
+and are not described in this section.
+
tstrlist::tstrlist(int flags = 0) constructs a tstrlist
+object. The parameter flags can be a combination of
+the following constants:
+
+
SL_OWNOBJECTS - this tstrlist
+object will be responsible for freeing objects.
+
SL_SORTED - create a sorted list; the items are
+kept in an alphabetically sorted order. This allows to use a tstrlist
+object as an associative array.
+
SL_DUPLICATES - allow duplicate keys. Must be combined
+with SL_SORTED.
+
SL_CASESENS - perform case-sensitive search. By
+default the search is case-insensitive.
+
+
void tstrlist::ins(int index, string key, X* obj)
+inserts a key/object pair into a list at the position index.
+For sorted lists index must be equal to the value returned
+by search() for the given key.
+A common pattern of using ins() on sorted lists is to
+call search() for a key to determine whether an object
+associated with a given key exists, then either insert a new object at the position
+pointed to by search() or to get the existing object
+at that position.
+
void tstrlist::put(int index, string key, X* obj)
+puts (replaces) the key/object pair at the position index.
+
void tstrlist::put(int index, X* obj) puts obj
+at the position index. The key at this position remains
+unchanged.
+
int tstrlist::add(string key, X* obj) on sorted lists
+this method performs search and inserts the key/object pair at a proper position
+to keep the list in a sorted order. For ordinary (unsorted) lists this method
+adds the key/object pair at the end of a list.
+
X* tstrlist::operator [](int index) returns an object
+at the position index.
+
string tstrlist::getkey(int index) returns the key
+value at the position index.
+
void tstrlist::del(int index) deletes the key/object
+pair at the position index.
+
int tstrlist::indexof(string key) determines the index
+of a given key. This function performs binary search
+on sorted lists, or otherwise linear search on unsorted lists. Returns -1 if key
+is not found.
+
int tstrlist::indexof(void* obj) determines the index
+of a given object. Always uses linear search. Returns -1 if obj
+is not found.
+
int tstrlist::put(string key, X* obj) is a universal
+method of adding, replacing and removing objects in a sorted list. Put()
+performs search by a given key and inserts obj
+if the key doesn't exist in a list, replaces the old object with obj
+if the key was found in a list, or deletes the key/object pair if the parameter
+obj was NULL.
+
X* tstrlist::operator [](string key) returns an object
+associated with a given key. Works only on sorted lists.
+Returns NULL if key is not found.
+
void tstrlist::del(string key) deletes the key/object
+pair for a given key. Works only on sorted lists.
+
bool tstrlist::search(string key, int& index)
+performs binary search on a list. Returns true if key
+is present in a list. The output parameter index contains
+either the position at which key was found, or otherwise
+the position where key must be inserted to preserve
+the sorted order.
+ 64-bit integers: most compilers provide long long
+type modifier for declaring 64-bit integers, but at least two of them, Microsoft
+C/C++ and Borland C/C++, use quite unaesthetic keyword __int64.
+PTypes offers its own wrapper type large (in <pport.h>),
+which is a portable signed 64-bit integer. Unsigned large
+or ularge are also available. For portable 64-bit string
+conversions use itostring() and stringto*()
+declared in <ptypes.h>.
+
Another incompatibility exists for printf-style formatted output routines:
+Windows uses "I64" type modifier for 64-bit integers, whereas standard-compliant
+libraries recognize "ll" (ell-ell). PTypes solves this problem in its
+outstm::putf() method: you can safely use "ll"
+modifier on all platforms.
+
End-of-line sequences: PTypes input stream routines automatically recognize
+three types of EOL sequences: LF (UNIX), CR (Mac) and CR-LF (DOS/Win) and set
+instm::eol property accordingly.
+
As for output streams, unlike many Windows-based C run-time libraries, PTypes
+does NOT make any translations and always sends your data as is. In order to generate
+an EOL sequence appropriate to the target platform (i.e. the system your application
+is running on), use outstm::puteol() and outstm::putline(string).
+Some protocols and data formats, however, require some predetermined EOL sequence
+regardless of the target platform, e.g. HTTP always uses CR-LF, PostScript uses
+CR, etc. In such cases you can use standard C escape sequences to send appropriate
+codes to the output stream, e.g. "\r\n" or "\r".
+
File names: you can always use UNIX-style path names (with slash '/')
+when working with files through PTypes streaming classes. Since drive specifiers
+in path names are specific only to Windows, try to use relative paths in your
+program whenever possible. For additional notes on pipe naming, please see introduction
+for the namedpipe class.
+
Arithmetic operations and multithreading: a simple increment and decrement
+operation (i++ and i--) in a multithreaded application may NOT be thread-safe
+on most RISC CPU's and on i386-based multiprocessor systems. Even if you are not
+interested in the resulting value of an increment/decrement operation, still use
+PTypes pincrement() and pdecrement()
+on variables shared between threads.
+
Performance of dynamic strings: most of the string operations rely on
+dynamic memory reallocation. PTypes tries to avoid frequent use of physical reallocation
+by aligning the dynamic string buffers on 16, 32, 64 ... 4096-byte boundaries,
+depending on the logical size of a string. Physical reallocation highly depends
+on the performance of the standard realloc() function,
+which may vary from OS to OS. Linux, for example, performs reallocations up to
+30 times faster than Windows on a similar computer. PTypes dynamic strings also
+depend on the performance of the atomic increment and decrement functions (see
+below).
+
Performance of atomic increment/decrement/exchange operations: on SPARC, PowerPC
+and i386-based platforms PTypes implements atomic operations using CPU instructions. On all other platforms these operations use slower mutex
+locking (see src/patomic.cxx).
+
Reentrancy of synchronization objects: the POSIX standard does not specify
+whether synchronization objects should be reentrant or not. For instance, if you
+use recursive locks on a mutex object the behaviour of your application may vary
+on different systems. Most POSIX systems, such like Linux, provide a faster but
+non-reentrant implementation of synchronization primitives.
+
Read/write lock portability issues: the rwlock
+class is built on top of the POSIX rwlock interface, wherever available, or uses
+the internal implementation (see src/rwlock.cxx). Various
+system-level implementations use different prioritization schemes: they may be
+'fair' with respect to both reader and writer threads, or a higher priority may
+be given to one of them. Unfortunately the behaviour of rwlock is not standardized
+yet. PTypes' internal implementation uses the 'fair' scheme which we believe is
+the best and the only DoS-proof (denial-of-service) among other algorithms. On
+the other hand, replacing the system-level rwlock with our own implementation
+on all systems would lead to a preformance penalty compared to using 'native'
+objects. We set this issue aside for some time and we are open to discuss it with
+interested/experienced developers.
When a recoverable error occurs during input/output operations, an exception
+of type (estream*) is thrown. The exception object
+contains an error message which can be shown to the user. The message is accessible
+through estream::get_message().
+
The example below shows how to catch an exception and recover normal execution
+of the program:
The estream class provides a system error code in
+UNIX errno semantics through estream::get_code(). On
+Windows most error codes are translated into corresponding UNIX errno codes. Less
+frequently occurring errors are translated to a generic errno code EIO. When an
+estream object returns EIO, the actual message string
+is constructed based on the status code of the stream. For example, if the status
+code was IO_OPENING, the error code was EIO, the message would be "Couldn't
+open [filename]". For explanations on what each errno code means,
+see comments in src/piobase.cxx source file.
+
Estream also provides a reference to the stream object
+that raised the error condition through estream::get_errstm().
Example 1. This simple program creates a new file and writes a string
+to it.
+
+
#include <pstreams.h>
+
+USING_PTYPES
+
+int main()
+{
+ // the outfile object is declared and constructed outside
+ // the try...catch clause, since the exception object
+ // contains a reference to the stream that caused the error.
+ // besides, stream constructors and destructors in PTypes
+ // never throw exceptions.
+ outfile f(fname);
+
+ f.set_bufsize(1024); // the default value in this version is 8192
+
+ try
+ {
+ f.open();
+ f.put("This is a test file.");
+ f.close();
+ }
+ catch (estream* e)
+ {
+ perr.putf("File error: %s\n", e->get_message());
+ delete e;
+ }
+ return 0;
+}
+
+
+
+
Example 2. This program reads a C source, extracts identifiers and builds
+a usage dictionary. It does not understand C comments and string literals though,
+but can be easily improved to understand them too.
+
+
#include <ptypes.h>
+#include <pstreams.h>
+
+USING_PTYPES
+
+const cset letters("_A-Za-z");
+const cset digits("0-9");
+const cset identchars = letters + digits;
+const cset otherchars = !letters;
+
+void main(int argc, char* argv[])
+{
+ tstrlist<void*> dic(SL_SORTED | SL_CASESENS);
+
+ infile f(argv[1]);
+
+ try
+ {
+ f.open();
+
+ while (!f.get_eof())
+ {
+ char c = f.preview();
+
+ // a C identifier begins with a letter
+ if (c & letters)
+ {
+ // ... and may contain letters and digits
+ string ident = f.token(identchars);
+ int i;
+ if (!dic.search(ident, i))
+ dic.ins(i, ident, 0);
+ }
+
+ else
+ // ignore everything else
+ f.skiptoken(otherchars);
+ }
+
+ }
+
+ catch (estream* e)
+ {
+ pout.putf("Error: %s\n", e->get_message());
+ delete e;
+ }
+
+ // now print the dictionary
+ for (int i = 0; i < dic.get_count(); i++)
+ pout.putline(dic.getkey(i));
+}
+
The stream input/output module, which is an integral part of PTypes, declares
+a family of classes that implement abstract functionality of stream-oriented data
+processing. The main features of this module include:
+
+
Text processing utilities, such like token extraction using character sets.
+
Buffering: both input and output streams can be buffered to speed up operation.
+
Scalability: the functionality of either input or output streams can be overridden
+by providing several low-level routines for the given device or communication
+protocol.
+
System-independent error handling: regardless of the system on which the
+library is compiled, the error codes are presented in UNIX "errno" semantics.
+
+
The basic class iobase encapsulates features
+common to both input and output, such like buffering, event and error handling,
+etc. This class is derived from component. Two descendant
+classes -- instm and outstm
+-- specialize in data input and output respectively, as well as provide simple
+and powerful text processing utility methods.
All recoverable error conditions generate exceptions of class (estream*).
+See Error handling for details.
+
Some encapsulated structure fields in this module are accessed through get_X()
+and set_X() function pairs. To simplify documentation,
+we use a single description in the form get/set_X()
+for such fields, and sometimes we refer to them as properties.
+
The stream i/o class family is declared in <pstreams.h>.
This class derives all public methods and properties from iobase
+and instm, and in addition, defines the following:
+
infile::infile( [ const string& filename ] ) creates
+an input file stream, but does not open the file. Filename
+is optional and can be set later, using set_filename().
+
void infile::pipe(outfile& peer) creates a local
+pipe. This function sets up and opens the infile and
+outfile objects so that a thread can asynchronously
+pass data to another thread within one process. Note that pipe()
+opens both streams; subsequent calls to open(), close()
+or cancel() shuts down the pipe. Local pipe is a slower
+alternative to the message queue. The only advantage of local pipes is that they
+provide a standard streaming interface.
+
string infile::get/set_filename(string) sets the
+file name. set_filename() closes the stream prior to
+assigning the new value.
Use inmemory to read data from a dynamic memory buffer
+by means of the streaming interface. Inmemory is derived
+from instm.
+
inmemory::inmemory(string mem) - constructs an inmemory
+object. The string passed through the mem parameter
+will be used by this object as a data source.
+
string inmemory::get/set_strdata(string) - gets or
+sets the memory buffer. Set_strdata() closes the stream
+prior to assigning the new value.
#include <pstreams.h>
+
+class instm: iobase {
+ bool get_eof();
+ bool get_eol();
+ char preview();
+ char get();
+ void putback();
+ string token(const cset& chars [, int limit ] );
+ int token(const cset& chars, char* buf, int size);
+ string line();
+ string line( [ int limit ] );
+ int line(char* buf, int size);
+ void skipline();
+ void skiptoken(const cset& chars);
+ int skip(int numbytes);
+ int read(char* buf, int count);
+}
+
+
+
This class implements the basic functionality of input streams. Instm
+is derived from iobase and inherits all its
+public methods and properties. All methods of instm
+except read() and get_eof()
+require buffering.
+
bool instm::get_eof() returns true
+if the end of file is reached.
+
bool instm::get_eol() returns true
+if the file pointer is currently at the end of a line. Since operating systems
+use different end-of-line codes or combinations of codes, it is recommended to
+check the end-of-line status using this property and skip the end-of-line sequence
+by calling skipline() method.
+
char instm::preview() returns the next character
+from the stream but does not advance the file pointer. If the pointer is at the
+end of file, preview() returns eofchar
+(null character).
+
char instm::get() returns the next character from
+the stream. If an attempt is made to read beyond the file (i.e. if the property
+eof is set), this method returns eofchar
+(null character).
+
void instm::putback() puts the last retrieved character
+back to the stream; a faster equivalent to seek(-1, IO_CURRENT).
+This function can be called only after a call to get()
+if the latter did not return an eof character.
+
string instm::token(const cset& chars [, int limit ] )
+reads the next token that only contains characters of the given set chars.
+The optional parameter limit specifies the maximum number
+of bytes to read. If the token exceeds the limit, an exception (estream*)
+is thrown with error number ERANGE.
+
int instm::token(const cset& chars, char* buf, int size)
+-- this version of token() reads the next token to the
+given buffer buf. The number of characters is limited
+to size. This method returns the actual number of characters
+read from the stream which can not be greater than size.
+Unlike the other version of token(), does not throw
+exceptions if the token exceeds the limit, but rather truncates it to size.
+Note: this function does not put a terminating null symbol in the buffer.
+
string instm::line( [ int limit ] ) reads the current
+line from the stream. The end-of-line code(s) are not included in the returning
+value, however, line() skips them and sets the file
+pointer at the beginning of the next line. The optional parameter limit
+specifies the maximum number of bytes to read. If the token exceeds the limit,
+an exception (estream*) is thrown with error number
+ERANGE.
+
int instm::line(char* buf, int size) -- this version
+of line() reads the next line from the stream to the
+buffer buf. The number of characters is limited to size.
+This method returns the actual number of characters read from the stream which
+can not be greater than size. Unlike the other version
+of line(), does not throw exceptions if the token exceeds
+the limit, but rather truncates it to size. Note: this
+function does not put a terminating null symbol in the buffer.
+
void instm::skiptoken(const cset& chars) works like
+previous versions of token() except that the token string
+is not returned. Can be safely used to skip very large tokens.
+
void instm::skipline() skips the current line and
+sets the file pointer at the beginning of the next line.
+
int instm::skip(int numbytes) skips the specified
+number of bytes from the input stream.
+
int instm::read(char* buf, int count) reads count
+bytes from the stream and stores them in the buffer buf.
+ Returns the number of bytes actually read. This method does
+not require buffering.
#include <pstreams.h>
+
+typedef void (*iostatusevent)(iobase* sender, int code);
+
+class iobase {
+ void open();
+ void close();
+ void cancel();
+ large tellx();
+ int tell();
+ large seekx(large newpos, ioseekmode mode = IO_BEGIN);
+ int seek(int newpos, ioseekmode mode = IO_BEGIN);
+ string get_streamname();
+ bool get/set_active(bool);
+ bool get/set_cancelled(bool);
+ int get/set_bufsize(int);
+ int get_status();
+ iostatusevent get/set_onstatus(iostatusevent);
+}
+
+
+
The methods and properties of this abstract class are common to all stream
+interfaces in PTypes.
+
void iobase::open() opens (activates) the stream.
+
void iobase::close() closes (deactivates) the stream.
+This method is called automatically during destruction of an object. Close()
+calls flush() for output streams. To simplify error
+handling in user programs, this method never throws exceptions. If you want to
+check for errors during the last write operation, call flush()
+explicitly before closing the stream.
+
void iobase::cancel() closes the stream and sets the
+cancelled property to true. In communication streams
+this method may close the connection immediately, unlike close(),
+which always tries to flush data buffers and then close the stream gracefully.
+
large iobase::tellx() returns either the current position
+in the target media (usually file) or, for communication channels, the number
+of bytes transfered from the beginning of the session.
+
int iobase::tell() left for compatibility; returns a 32-bit file offset or raises an exception if the value doesn't fit the int type.
+
large iobase::seekx(large newpos, ioseekmode mode = IO_BEGIN) changes the media pointer (e.g. file pointer). Possible values for mode are: IO_BEGIN, IO_CURRENT and IO_END. This function first tries to position the
+stream pointer within the scope of the I/O buffer. If the new pointer is out of
+buffer's range, seekx() then tries to change the physical
+pointer of the target media. Note that physical positioning is not supported by
+some stream types, e.g. by ipstream.
+
int iobase::seek(int newpos, ioseekmode mode = IO_BEGIN) left for compatibility; tries to seek in 32-bit mode and return a 32-bit file offset. Raises an exception if the return value doesn't fir the int type.
+
string iobase::get_streamname() returns a string
+containing either a file name, an URL, or some other string describing the underlying
+media or object. Useful for diagnostics messages.
+
bool iobase::get/set_active(bool) indicates whether
+the stream is active. Setting this property to true
+or false opens or closes the stream respectively.
+
bool iobase::get/set_cancelled(bool) returns true
+if the stream was closed using cancel().
+
int iobase::get/set_bufsize(int) sets the buffer
+size for the stream. Setting this property to -1 will assign some reasonable default
+value, which may vary depending on the operating environment, available memory,
+etc. Note: text input routines require buffering (see instm).
+
int iobase::get_status(int) returns the current status
+of the stream. The value of this property can be one of the following: IO_CREATED,
+IO_OPENING, IO_OPENED, IO_READING, IO_WRITING, IO_EOF, IO_CLOSING, IO_CLOSED.
+Derivative classes may add other status codes, e.g. see ipstream.
+
iostatusevent iobase::get/set_onstatus(iostatusevent)
+-- sets or returns user-defined callback function. The callback function is called
+whenever the status of the stream is changed (see also get_status()).
The lofgile class inherits all public methods and
+properties from outfile, but differs in the
+following:
+
+
The append property is set to true
+by default.
+
The buffer size is set to 0 by default.
+
The logfile::putf() function is thread-safe: you
+can call this function for the same logfile object from concurrent threads.
+
+
logfile::logfile( [ const string& filename, bool append
+= true ] ) creates an output file stream, but does not open the file. When
+opening a file with open(), the file pointer is positioned
+at the end of the file, unless append is set to false.
+Filename and append parameters
+are optional.
+
void logfile::putf(const char* fmt, ...) is a thread-safe
+version of outstm::putf().
MD5, the message digest algorithm described in RFC 1321, computes a 128-bit
+sequence (sometimes called 'message digest', 'fingerprint' or 'MD5 checksum')
+from arbitrary data. As stated in RFC 1321, it is conjectured that it is computationally
+infeasible to produce two messages having the same message digest, or to produce
+any message having a given prespecified target message digest. MD5 can be viewed
+as a one-way encryption system and can be used, for example, to encrypt passwords
+in a password database.
+
The MD5 fingerprint is more often converted to so-called ASCII-64 form in order
+to conveniently store it in plain text environments and protocols. Thus, the 128-bit
+binary sequence becomes a 22-character text string consisting of letters, digits
+and two special symbols '.' and '/'. (Note that this is not the same as Base64
+encoding in MIME).
+
In order to compute a MD5 fingerprint you first create a stream object of type
+outmd5 and then send data to it as if it was an ordinary
+output file or a socket stream. After you close the stream, you can obtain the
+fingerprint in ASCII-64 form using the object's get_digest()
+method.
+
The implementation of MD5 is derived from L. Peter Deutsch's work.
+
This class derives all public methods and properties from iobase
+and outstm, and in addition defines the following:
+
outmd5::outmd5() creates a bufferless MD5 stream.
+
outmd5::outmd5(outstm* outthru) creates a MD5 stream
+and attaches an output stream outthru to it. Everything
+sent to the MD5 stream will also be duplicated to outthru.
+You may want, for example, to attach perr to your MD5
+stream for debugging purposes.
+
string outmd5::get_digest() closes the stream and
+returns the computed fingerprint in text form (ASCII-64).
+
const unsigned char* outmd5::get_bindigest() closes
+the stream and returns a pointer to a 16-byte buffer with the binary MD5 fingerprint.
The namedpipe class implements full-duplex streaming
+communication between processes, typically on one computer. It is built on top
+of Windows named pipes and local UNIX sockets. Namedpipe
+incorporates iobase, instm
+and outstm interfaces and adds one more property:
+the pipe name to connect to. PTypes named pipes are session-oriented.
+
Naming. Local sockets on UNIX can be created anywhere in the file system
+and generally behave like ordinary files, except that the input and output streams
+for a socket file is handled by the server process that created the socket. In
+contrast to UNIX, Windows allocates named pipes in a special directory (actually
+a share name) \\.\pipe, which is not visible to the
+end-user.
+
PTypes can accept both a file name and an absolute path name when creating
+a named pipe object, however, absolute paths have effect only on UNIX. If you
+specify a single file name on UNIX, PTypes will place the socket file in /tmp.
+That is, if you are writing a portable application, and you want the socket file
+to be placed in a directory other than /tmp on UNIX
+(e.g. some daemons running as root place their sockets in /var/run),
+you can specify the full path in both Windows and UNIX versions of your program:
+the library will ignore the path on Windows, since it can not place it other than
+in \\.\pipe, and will use it on UNIX.
+
Usage. Named pipes provide efficient means of interprocess communication.
+Many networking applications, typically SQL servers, offer both network sockets
+and local sockets as alternate ways of connecting to the service. For security
+reasons, a system administrator may choose to configure the service such that
+it will use only the local socket, or the service can be open to the network too
+if it provides strong authentication at application level.
+
Namedpipe and ipstream
+classes are compatible: both are full-duplex streams and are derived from instm
+and outstm simultaneously. Thus, PTypes allows you to
+easily combine both methods of communication in one application. Each thread serving
+connections on server-side of your application can accept two parameters of basic
+types instm and outsm; you
+can then pass either an ipstream or a namedpipe
+object (or any other compatible stream object) to the newly instantiated thread
+to serve the request coming from the client. Note that it is sometimes necessary
+to provide explicit typecast when assigning namedpipe
+or ipstream to outstm or instm.
+
Windows security note. Named pipes on Windows are open to the network,
+i.e. any computer can connect to a pipe through \\computer-name\pipe\...,
+where computer-name can be a NetBIOS name or even an IP address. Even though
+PTypes' interfaces do not allow you to connect to a remote named pipe for the
+sake of compatibility with UNIX, still, you should consider a situation when someone
+knowing the pipe name and the protocol you use, writes his own program to 'illegally'
+access your service on a Windows machine from a remote computer. Hence, for better
+security, your service should provide user authentication at application level
+(of course, unless it's a public service and is open anyway). Aside from security,
+network named pipes are much slower than any other networking protocol, such like
+TCP/IP, so we do not encourage using named pipes remotely in any case.
+
Unlike Windows, UNIX local sockets can never be accessed from a remote computer
+even through a NFS-mounted directory. Note that remote access to named pipes on
+Windows can be disabled by stopping all Windows networking services and leaving
+only the transport-level protocol stacks.
+
Local unnamed pipes for exchanging data within one process can be created using
+infile::pipe().
+
Interface. Namedpipe is compatible with iobase,
+instm and outstm
+and in addition, defines the following:
+
namedpipe::namedpipe( [ string pipename ] ) creates
+a named pipe object and optionally assigns the pipe name. When creating a namedpipe
+object that will be passed to npserver::serve(), it
+is not necessary to assign a pipe name.
+
string namedpipe::get/set_pipename(string) gets or
+sets the pipe name. When assigning a new pipe name, set_pipename()
+first closes the stream.
The npserver class is used on the server side of
+an interprocess communication channel. It listens to connections on the specified
+named pipe or a local UNIX socket and for each connection provides a namedpipe
+object for exchanging data with the peer.
+
Please, read the introduction to namedpipe
+first.
+
npserver::npserver(string pipename) creates a named
+pipe server object. The named pipe or a local socket is actually created during
+the first call to serve() for this object.
+
bool npserver::serve(namedpipe& client, int timeout =
+-1) polls the pipe for connection requests. If there is a connection request
+from a client, serve() opens and prepares the supplied
+namedpipe object for communicating with the client, i.e. client
+will be active upon return from serve(). The second
+optional parameter timeout specifies the amount of time
+in milliseconds to wait for a connection request. If timeout is 0 serve()
+will return immediately; if it's -1 serve() will wait
+infinitely. This function returns true if there is a
+connection request and the client object is active,
+or false if the call has timed out.
This class derives all public methods and properties from iobase
+and outstm, and in addition defines the following:
+
outfile::outfile( [ const string& filename, bool append
+= false ] ) creates an output file stream, but does not open the file.
+When opening a file with open(), it is truncated to
+zero unless append property is set to true. Filename
+and append parameters are optional.
+
string outfile::get/set_filename(string) sets the
+file name. set_filename() closes the stream prior to
+assigning the new value.
+
bool outfile::get/set_append(bool) -- if set to true,
+the file pointer is set beyond the last byte of the file when opening the stream
+with open().
+
int outfile::get/set_umode(int) sets UNIX file mode
+when creating a new file. By default a file is created with 0644
+octal, which on UNIX means read/write access for the owner and read-only access
+for group members and all others. This property has no effect on Windows.
Use outmemory to write data to a dynamic memory buffer
+by means of the streaming interface. Outmemory is derived
+from outstm.
+
outmemory::outmemory(int limit = -1) creates an outmemory
+object. The memory buffer grows as data is written to the stream. You can optionally
+limit the size of the memory buffer to limit (-1 means
+unlimited).
+
string outmemory::get_strdata() returns data written
+to the stream as a dynamic string and closes the stream.
This class implements the basic functionality of output streams. Outstm
+is derived from iobase and inherits all its
+public methods and properties.
+
End-of-line sequences are not translated when you send data through the output
+methods. To write an end-of-line sequence appropriate to the given operating environment
+use puteol() instead.
+
void outstm::putf(const char* fmt, ...) is a printf-style
+output method. PTypes supports a subset of format specifiers common to all platforms:
+<blank>, '#', '+' and '-' formatting flags, 'L', 'h', 'l' and 'll' format
+modifiers, and the following standard format specifiers: cdiouxXeEfgGps.
+In addition, PTypes supports a format specifier a for
+IP addresses (ipaddress type) and also t
+and T for timestamps (datetime
+type). Note that some compilers require to explicitly cast ipaddress
+arguments to long type, and also string
+arguments to const char* (or pconst).
+
void outstm::put(char c) writes the character c
+to the stream.
+
void outstm::put(string str) writes the string str
+to the stream.
+
void outstm::puteol() writes an end-of-line sequence
+to the stream. The actual sequence depends on the platform the library was compiled
+on. May flush data if the property flusheol is set to
+true.
+
void outstm::putline(string str) writes the string
+str to the stream followed by an end-of-line sequence,
+as in call to puteol().
+
int outstm::write(const char* buf, int count) writes
+count bytes from the buffer buf
+to the stream.
+
void outstm::flush() writes the remaining data in
+the buffer to the media, if any. This method is called automatically when the
+stream is being closed.
+
bool outstm::get/set_flusheol(bool) -- set this property
+to true if you want each line to be written to the media or communication stream
+immediately. Default is false.
PTypes declares four static stream objects for standard input, output, error
+and null devices - pin, pout,
+perr and pnull respectively.
+These objects can be used in place of the standard C or C++ input/output interfaces.
+Pnull is an output stream that discards any data written
+to it. The putf() method in standard output objects
+pout and perr is atomic with
+respect to multithreading.
A string object can be constructed in 5 different ways:
+
+
+
default constructor string() creates an empty string.
+
+
+
copy constructor string(const string& s) creates
+a copy of the given string s. Actually this constructor
+only increments the reference count by 1 and no memory allocation takes place.
+
+
+
string(char c) constructs a new string consisting
+of one character c.
+
+
+
string(const char* s) constructs a new string object
+from a null-terminated string. If s is either NULL
+or is a pointer to a null character, an empty string object is created. This constructor
+can be used to assign a string literal to a string object (see examples below).
+
+
+
string(const char* s, int len) copies len
+bytes of data from buffer s to the newly allocated string
+buffer.
+
+
+
Destructor ~string() decrements the reference count
+for the given string buffer and removes it from the dynamic memory if necessary.
#include <ptypes.h>
+
+string itostring(<ordinal> value);
+string itostring(<ordinal> value, int base, int width = 0, char pad = ' ');
+large stringtoi(const string& s);
+large stringtoie(const string& s);
+ularge stringtoue(const string& s, int base);
+string lowercase(const string& s);
+
+
+
+PTypes provides 3 different string-to-int conversion functions: stringtoi(),
+stringtoie() and stringtoue().
+The first function stringtoi() is for non-negative decimal
+numbers; it returns -1 on error. The other two functions with a suffix 'e' in
+their names ('e' is for 'exception') may throw exceptions, but they accept the
+full range of 64-bit values.
+
These functions replace the CRTL functions atoll()
+and strtoll() which are not implemented on all systems.
+
Both function families, string-to-int and int-to-string, accept numeration
+bases in the range 2 to 64. The 64-digit numeration uses all digits, letters and
+also '.' and '/'. It may be useful for representing, for example, MD5 checksums
+in a compact printable form (see function outmd5::get_digest()
+in src/pmd5.cxx).
+
+
string itostring(<ordinal> value) converts
+the given ordinal value to a string. Various overloaded
+versions of this function accept ordinal values of different sizes and signness.
+
string itostring(<ordinal> value, int base,
+int width = 0, char pad = ' ') converts an integer value to a string with
+the numeration base specified by base, which can be
+in the range 2 - 64. To right-justify the resulting string you can specify width
+and pad parameters. If the numeration base is greater
+than 36 in addition to digits and letters itostring()
+uses '.' and '/' characters and also lowercase letters. For numeration bases other
+than 10 the parameter value is always treated as unsigned.
+
large stringtoi(const string& s) converts a string
+to a 64-bit integer. This function accepts only positive decimal large numbers
+and 0. It returns -1 if the string does not represent a valid positive number
+or an overflow occurred.
+
large stringtoie(const string& s) converts a string
+to a 64-bit integer. This function accepts signed decimal large numbers.
+Unlike stringtoi(), this function may throw an exception
+of type (econv*) if the string does not represent a
+valid number or an overflow occurred.
+
unsigned large stringtoue(const string& s, int base)
+converts a string to an unsigned 64-bit integer using the numeration base specified
+by base. This function accepts unsigned large numbers.
+It may throw an exception of type (econv*) if the string
+does not represent a valid number or an overflow occurred. Base
+can be in the range 2 - 64. For numeration bases from 2 to 36 this function uses
+digits and letters, and the letter case is insignificant. For numeration bases
+grater than 36, '.', '/' and lowercase letters are used additionaly.
+
string lowercase(const string& s) converts all
+characters of the given string s to lower case. The
+current version of the library "understands" only lower ASCII characters;
+all other characters remain unchanged. This function can effectively detect if
+all characters in the string are already in lower-case to avoid unnecessary string
+allocations.
The string class implements dynamically allocated
+reference-counted 8-bit character strings. The string
+class is a mixture of L-type and null-terminated strings: it has both the length
+indicator and the terminating null-symbol. The length of a string is theoretically
+limited to INT_MAX, and practically is limited to the
+amount of virtual memory space available to the application.
+
A string object itself contains only a reference to the first character of
+the string buffer. A string object can be implicitly converted to a null-terminated
+string, either a variable or passed as an actual parameter, thus allowing to combine
+both PTypes strings and traditional C strings in your application. A string object
+converted to char* or const char*
+never returns NULL pointers: it guarantees to always
+point to some data, even if the string is zero-sized.
+
The reference counting mechanism works transparently (known also as
+copy-on-write) and safely with regard to multithreading. You can manipulate
+string objects as if each object had its own copy of string data. Whenever you
+modify a string object the library safely detaches the buffer from all other string
+objects that may be using the same buffer and creates a unique copy so that changes
+won't affect the other "holders" of this string.
+
NOTE on multithreading: the dynamic string objects themselves are NOT
+thread-safe. In other words, each thread can manipulate objects (variables) of
+type string only within their scope. However, it is
+safe to pass strings as (copy) parameters when, for example, sending a message
+to a concurrent thread through a message queue. Whenever the recipient thread
+tries to modify the string, the shared data buffer is safely detached.
#include <ptypes.h>
+
+// get/set length and misc.
+int length(const string& s);
+char* setlength(string&, int);
+char* unique(string&);
+void clear(string& s);
+bool isempty(const string& s);
+
+// concatenate
+void concat(string& s, const char* sc, int catlen);
+
+// copy (get substring by position and length)
+string copy(const string& s, int from [, int cnt ] );
+
+// insert string or character
+void ins(const char* s1, string& s, int at);
+void ins(char s1, string& s, int at);
+void ins(const string& s1, string& s, int at);
+void ins(const char* s1, int s1len, string& s, int at);
+
+// delete substring
+void del(string& s, int at [, int cnt ] );
+
+// find substring or character
+int pos(const char* s1, const string& s);
+int pos(char s1, const string& s);
+int pos(const string& s1, const string& s);
+int rpos(char s1, const string& s);
+
+// compare substring
+bool contains(const char* s1, const string& s, int at);
+bool contains(char s1, const string& s, int at);
+bool contains(const string& s1, const string& s, int at);
+bool contains(const char* s1, int s1len, const string& s, int at);
+
+
+
int length(const string& s) returns the actual length
+of the string, not counting the terminating null-symbol.
+
char* setlength(string&, int) changes the actual length
+of the string. The content of the original string is preserved, however the content
+of extra characters added during reallocation is undefined. This function returns
+a pointer to a unique buffer (i.e. refcount is 1), like function unique()
+below. Even if the length of the string is not changing, setlength()
+guarantees to make the string unique.
+
char* unique(string&) makes the string buffer unique,
+i.e. a new buffer is allocated and data is copied if necessary, so that the reference
+count after calling this function is guaranteed to be 1. All string manipulation
+functions call unique() whenever a modification is made
+on the string buffer. You may need to call this function explicitly to obtain
+a character pointer to the buffer; in all other cases reference counting mechanism
+works transparently.
+
bool isempty(string&) returns true if the given
+string is empty. Using this function is preferable to comparing the string with
+empty string literal "".
+
clear(string&) makes the given string empty. Using
+this function is preferable to assigning an empty string literal "".
+
concat(string& s, const char* sc, int catlen) adds
+the given buffer sc of length catlen
+to the string object s. Use operators + and += instead
+to concatenate characters, null-terminated strings and string objects.
+
string copy(const string& s, int from [, int cnt ] )
+returns a substring of s starting from position from
+and containing cnt characters. If cnt
+is omitted, the rest of the string s starting from position
+from is returned.
+
ins(..., string& s, int at) inserts a character, a
+null-terminated string, a string object or a buffer with specified length into
+string object s at the given position at.
+If the position is out of bounds, ins() does nothing.
+
del(string& s, int at [, int cnt ] ) deletes cnt
+characters starting from position at of the string s.
+If cnt is omitted, the rest of the string starting from
+at is deleted.
+
int pos(..., const string& s) returns the position
+of the first occurrence of a character, a null-terminated string or a string object
+(first parameter) in the source string s, or returns
+-1 if the substring is not found. Function rpos() performs
+reverse-search.
+
bool contains(..., const string& s, int at) returns
+true if the given character, null-terminated string or string object (first parameter)
+equals the substring of s at the given position at.
The string class defines the following binary operators: assignment (=),
+concatenation (+), concatenation with assignment (+=)
+and comparison (==, !=). At
+least one of the operands (either left or right) must be of type string.
+Another operand can be one of the following: char, char*
+or string.
+
Indexed access operator allows to store or retrieve a value of an individual
+character. The index is 0-based. When compiled with either DEBUG
+or CHECK_BOUNDS conditional symbol, bounds checking
+is performed for the indexed access; if the index is out of bounds (i.e. less
+than 0 or equals to or greater than the length of the string), an unrecoverable
+error is raised. The non-debugging version of the library never checks for index
+overlfows, thus making your program somewhat faster but less safe.
A string object can be assigned to a variable or passed as an actual parameter
+of type const char* implicitly (by default). Such assignments
+should be used carefully, since the library does not keep track of whether a char
+pointer refers to the given string buffer. To make sure that a char pointer refers
+to a valid string buffer, always make the scope of a char pointer variable smaller
+than or equal to the scope of a string object. In most cases passing a string
+object to a system or API call is safe (see examples below). This typecast operator
+does not perform any actions and simply returns a pointer to the string buffer.
+
The value of the char pointer is guaranteed to be non-NULL. Even if the string
+is empty, the char pointer will refer to a null-symbol.
+
A string buffer can not be modified through a constant char pointer. If you
+want to modify the string buffer through a char pointer, use unique(string&)
+function instead. This function always returns a reference to a unique string
+buffer (i.e. when the reference count is 1).
+
Compatibility note: MSVC and GCC may treat type casts in different ways
+when passing a string object to a function that takes (...) parameters, e.g. printf()
+or outstm::putf(). You should explicitly instruct the
+compiler to cast the string object to (const char*)
+to avoid this problem. PTypes provides a shorter typedef pconst
+for this.
+
Examples
+
+
+void assignment_example()
+{
+ string s = "abcdef";
+ const char* p = s;
+ // do string manipulation here...
+}
+
+void function_call_example()
+{
+ string s = "abcdef";
+ puts(s);
+ printf("%s\n", pconst(s));
+}
+
#include <ptime.h>
+
+typedef large datetime;
+
+bool isleapyear(int year);
+int daysinmonth(int year, int month);
+int daysinyear(int year, int month);
+int dayofweek(datetime d);
+bool isdatevalid(int year, int month, int day);
+datetime encodedate(int year, int month, int day);
+bool decodedate(datetime d, int& year, int& month, int& day);
+
+
+
bool isleapyear(int year) determines whether year
+is a leap year.
+
int daysinmonth(int year, int month) returns the
+number of days in month (1 - 12). The parameter year
+is needed to determine whether it's a leap year in order to return the correct
+number of days for February. The returned value can be in the range 28 - 31. If
+month is out of the allowed range (1 - 12) this function
+returns 0.
+
int daysinyear(int year, int month) returns the number
+of days since the beginning of the year up to the month
+(1 - 12), inclusive. If month is 12 the function will
+return the total number of days in the year. If month
+is out of the allowed range (1 - 12) this function returns 0.
+
int dayofweek(datetime d) returns the day of the
+week (0 - 6) for the given date d. 0 corresponds to
+Sunday, 1 - Monday etc. This function does not check the value of d
+for validity, it always returns some result in the range 0 - 6.
+
bool isdatevalid(int year, int month, int day) checks
+the values of year, month
+and day for validity. This function takes into account
+that day must be in the correct range depending on year
+and month. Also, it checks whether year
+is in the allowed range 1 - 9999.
+
datetime encodedate(int year, int month, int day)
+returns a datetime value, i.e. the number of milliseconds
+since the beginning of the calendar up to the midnight of month/day/year.
+This value can then be added to the value returned by encodetime()
+to form the exact time stamp with millisecond precision, or it can be used alone
+as a calendar date value without the time correction.
+
bool decodedate(datetime d, int& year, int& month, int&
+day) is the inverse version of encodedate():
+it breaks the value of d into its year,
+month and day. This function
+returns false if the value of d
+is invalid.
datetime now(bool utc = true) returns the current
+date/time. The returned value can be either the local time or the Universal Coordinated
+Time (UTC, aka GMT), depending on the parameter utc.
+It is recommended to manipulate with the UTC time internally in your application
+whenever possible (f.ex. when it's not needed to display the time to the user),
+since in many countries the local time may be automatically adjusted when entering
+or leaving the daylight saving period, which may confuse your application. On
+the contrary, the UTC time never changes. That's why all modern operating systems
+rely on the UTC time internally. (See also tzupdate()
+below for additional notes).
+
int days(datetime d) returns the number of days since
+the beginning of the calendar contained in the datetime
+value d.
+
int msecs(datetime d) returns the number of milliseconds
+since midnight contained in the datetime value d.
+
datetime mkdt(int days, int msecs) calculates the
+datetime value from days and msecs
+parameters. Days is the number of days since the beginning
+of the calendar, and msecs is the number of milliseconds
+since midnight. No checks are made for validity of these values. There exists
+an easier way to build a datetime value using encodedate()
+and encodetime() functions having the year, month and
+day numbers, as well as (not necessarily) the hour, minute, second and millisecond
+values.
+
bool isvalid(datetime d) checks a datetime
+value for validity. The value of d is valid if it holds
+a time stamp between 01/01/0001 and 12/31/9999.
+
void tzupdate() updates the internal timezone information,
+which affects the value of local time returned by now()
+and nowstring(). If your application is supposed to
+be running infinitely (e.g. if it's a network daemon), you might want to update
+the internal timezone information from time to time in order to return correct
+local time to the user on DST adjustment days. Depending on the precision of local
+time you wish to show to the user, you can call this function, for example, every
+minute or every hour. Without this, the local time may become incorrect at the
+moment of DST adjustment, which occurs twice a year in most countries.
+
int tzoffset() returns the time zone offset in minutes.
+This value is negative in the West. Multiplying this value by 60000 (i.e. the
+offset in milliseconds) and adding it to the UTC datetime
+value will give the local time.
+
string dttostring(datetime d, const char* fmt) converts
+a datetime value to string representation as specified
+by fmt. The syntax of the format specifier is the same
+as for strftime() (please, refer to the corresponding
+manual pages in your programming environment). Note that there might be slight
+incompatibilities between different implementations of strftime().
+
+
string nowstring(const char* fmt, bool utc = true)
+returns a string representing the current time in a format specified by fmt.
+Utc specifies whether the local time or the UTC time
+is required. Like for dttostring(), the syntax of the
+format specifier fmt is the same as for the system function
+strftime().
+
datetime utodatetime(time_t u) converts UNIX time_t
+value to PTypes datetime.
The datetime data type and the accompanying utilities
+provide portable (system-independent) means of calendar and time manipulation.
+Datetime is equivalent to signed 64-bit integer type
+(large type in PTypes) which holds a time value with
+a millisecond precision in the range of dates 01/01/0001 through 12/31/9999. The
+value of a datetime variable represents the number of milliseconds since the beginning
+of the calendar, i.e. midnight January 1st, year 1. The upper limit is caused
+by the fact that the leap year rule may (and most likely will) change after the
+year 10000.
+
Variables of type datetime can be used in various
+ways: as a date/time stamp, as a difference between two events in the range from
+1 millisecond up to 10,000 years, as a date alone without the time (e.g. for calendar
+manipulation), and as a time value alone.
+
The date/time manipulation functions are divided into 3 groups in this documentation:
+general, date/calendar
+and time. Most of these functions work with parameters
+of type datetime. The prototypes and other declarations
+are in the header file <ptime.h>.
+
Here are some examples of using datetime and the
+accompanying utilities:
+
+
#include <pasync.h> // for psleep()
+#include <ptime.h>
+#include <pstreams.h>
+
+USING_PTYPES
+
+int main()
+{
+ // PTypes' birthday (birth moment, if you wish)
+ datetime d = encodedate(2000, 3, 30) + encodetime(13, 24, 58, 995);
+ // The format specifier %t is for timestamps
+ pout.putf("PTypes' birth moment: %t\n", d);
+
+ // now see how old is PTypes in milliseconds
+ datetime diff = now() - d;
+ int hours, mins, secs, msecs;
+ decodetime(diff, hours, mins, secs, msecs);
+ pout.putf("PTypes' life time: %d hours %d minutes %d seconds and %d milliseconds\n",
+ days(diff) * 24 + hours, mins, secs, msecs);
+
+ // measure the difference in milliseconds between two calls to now()
+ datetime m = now();
+ psleep(17); // sleep 17 milliseconds
+ pout.putf("A 17 millisecond dream lasted actually %d milliseconds\n", now() - m);
+ // this will show the actual precision of the clock on the given platform;
+ // Windows, f.ex., always shows the difference in 10 msec increments
+}
+
#include <ptime.h>
+
+typedef large datetime;
+
+bool istimevalid(int hour, int min, int sec, int msec = 0);
+datetime encodetime(int hour, int min, int sec, int msec = 0);
+bool decodetime(datetime d, int& hour, int& min, int& sec [, int& msec] );
+
+
+
bool istimevalid(int hour, int min, int sec, int msec =
+0) checks whether hour, min,
+sec and msec (millisecond)
+contain correct values in their respective ranges. The last parameter is optional.
+
datetime encodetime(int hour, int min, int sec, int msec
+= 0) returns a datetime value, i.e. the number
+of milliseconds since midnight. This function does NOT check the input parameters
+for validity. The value returned by this function can be added to the return value
+of encodedate() to form the exact time stamp for the
+given year, month, day, hour, minute, second and (optionally) millisecond.
+
bool decodetime(datetime d, int& hour, int& min, int& sec
+[, int& msec] ) splits the value of d into hour,
+minute, sec and msec
+(millisecond) passed since midnight. The last parameter is optional.
Units represent functionality similar to console
+applications in UNIX. Each unit has its own main() along
+with input and output 'plugs' - uin and uout,
+that may be mapped to the standard input and output, a local pipe or any other
+stream compatible with instm and outstm,
+respectively.
+
Each unit class must at least override the abstract method main().
+Overridden unit classes typically read input data from uin
+and send the result of processing to uout, like if they
+were console applications. By default uin and uout
+are attached to standard input and output. After instantiating a unit object you
+(the user of a unit class) may attach any instm-compatible
+stream to uin and any outstm-compatible
+stream to uout. In addition, units are able to connect
+to each other using local pipes, and thus form data processing chains within your
+application.
+
You may define other public methods or fields in your unit class that represent
+additional options. E.g. a regular expression parser unit may have a string field
+that represents the regular expression itself (see example below).
+
Units can be run either synchronously or asynchronously. In the latter case,
+a separate thread is created for executing unit's main()
+function. If connected to a pipe using connect(), the
+first unit in the chain runs within the scope of the calling thread, the others
+run in separate threads.
+
The unit class is a subclass of component,
+and thus it inherits reference counting and delete notification mechanisms from
+component. Unit is declared
+in <pstreams.h>.
+
This interface is available only in the multithreaded versions of the library.
+
+
compref<instm> unit::uin is a reference-counted
+pointer to an input stream, that is unit's input 'plug'. By default uin
+refers to the standard input object pin. Typically both
+uin and uout are assigned
+by the user of the unit after instantiating a unit object. You may assign dynamically
+allocated stream objects to uin and uout
+- they will be freed automatically by the 'smart' compref
+pointer.
+
compref<outstm> unit::uout -- same as uin;
+represents the output 'plug' of the unit.
+
virtual void unit::main() is unit's main code. Override
+this method to implement functionality of your mini-process. Note that code in
+main() must avoid accessing static/global data, since
+it may be executed in a separate thread. You may choose to write a reusable unit,
+i.e. when main() can be called multiple times for the
+same object, however main() is protected from overlapping
+(recursive) calls, which means, you need not to write reentrant code in this function.
+
virtual void unit::cleanup() -- override this method
+to perform finalization and cleanup of a unit. This function is guaranteed to
+be called even if main() threw an exception of type
+(exception*) or a derivative.
+
void unit::connect(unit* next) connects a unit object
+to another object using a local pipe. Multiple units can be connected to form
+a chain. A user then calls run() for the first object;
+all other members of the chain are started automatically in separate threads.
+
void unit::run(bool async = false) runs a unit object.
+This function calls main() for the given object and
+possibly for other units, if this is the first object of a chain. You can not
+call run() for an object which is not the first in a
+chain. If async is true, this
+function starts a unit in a separate thread and returns immediately. Use waitfor()
+to synchronize with the completion of a unit if started asynchronously.
+
void unit::waitfor() waits for the unit to terminate
+if run asynchronously. For unit chains, this method needs to be called only for
+the first object in a chain.
+
Example. Consider there is a unit type ugrep
+that performs regular expression matching and a unit type utextconv
+for converting various text formats. The code below demonstrates how to connect
+these units and run the chain.
+
+
#include <pstreams.h>
+
+#include "ugrep.h" // imaginary headers with unit declarations
+#include "utextconv.h"
+
+USING_PTYPES
+
+int main()
+{
+ ugrep grep;
+ grep.regex = "^abc";
+ grep.casesens = false;
+
+ utextconv textconv;
+ textconv.from = CONV_UNIX;
+ textconv.to = CONV_WIN;
+
+ // connect the two units and set up the input and output plugs.
+ grep.uin = new ipstream("somehost.com", 8282);
+ grep.connect(&textconv);
+ textconv.uout = new outfile("output.txt");
+
+ // now run the chain; will read input from the socket, pass
+ // through the grep and textconv units and write it to the
+ // output file.
+ grep.run();
+}
+
The unknown interface is the base for all interfaces
+(classes) in PTypes, exceptstring, cset,
+variant, mutex, rwlock,
+msgqueue and tpodlist. The
+unknown class has no semantic or functional meaning,
+except that deriving a class from unknown provides a
+simple mechanism of tracking the number of created/destroyed objects in a program.
+Historically, unknown was used in PTypes as a base for
+all list item types, however, starting from version 2.0 of the library this requirement
+is no longer in place.
+
The component class adds reference-counting functionality
+to unknown (see addref() and
+release() below). PTypes variants require objects to
+be derived from component instead of unknown
+to be able to make assignments and destruction of variants properly. See also
+compref below.
+
As an alternative to reference-counting component
+also provides so-called 'delete notification' mechanism. If object A holds a reference
+to object B, A can add itself to B's notification list to be notified when B is
+being destroyed. This allows A to take appropriate actions, e.g. invalidate the
+reference to B. A in this case can declare reference to B as a plain pointer.
+In other words, A should override its own virtual method freenotify(),
+and it also should call addnotification(this) for the
+object it holds reference to.
+
All stream objects in PTypes and aslo the unit class
+are derived from component.
+
A global variable objalloc is declared to keep track
+of the number of allocated objects (derived from unknown
+or component) in an application program, which can help
+you to find memory leaks or other potential bugs. The allocation counting works
+only in DEBUG mode. If the memory is cleaned up properly,
+this value should be zero upon program termination. You can write code, possibly
+enclosed within #ifdef DEBUG ... #endif, which checks
+whether this value is zero at the end of main().
component* addref(component* c) increments the reference
+counter for the given component object c. The return
+value is the same as c and is provided for convenience.
+
bool release(component* c) decrements the reference
+counter and destroys the object c if the counter reached
+0. Returns true if the object has been destroyed. Passing
+NULL to this function is not an error.
+
template <class T> class compref implements
+a 'smart' pointer to component (or any derivative class)
+with automatic reference counting. Use this template in place of a plain pointer
+declaration (e.g. compref<myclass> instead of
+myclass*) to automatically destroy objects as soon as
+there are no more references left. The behavior of compref
+pointers is similar to plain pointers, except that they perform additional actions
+when assigning new values to them.
+
virtual component::~component() destroys the component
+and calls freenotify() for each object in the notification
+list.
+
component::addnotification(component* c) adds object
+c to the notification list of this object. This object's
+destructor will then call c->freenotify().
+
component::delnotification(component* c) deletes object
+c from this object's notification list.
+
virtual component::freenotify(component* caller) is
+called by the caller's destructor for each object
+in the notification list. This method should be overriden in classes which add
+themselves to other objects' notification lists. The overridden method usually
+invalidates (assigns NULL) all references to caller.
A variant object can hold an associative array of variants. The variant changes
+its type to an array as soon as put() or aclear()
+is called for an object. When assigning variants, only a reference to an array
+is being copied, which means, modifying an array through one variant object affects
+all other objects that hold a reference to the same array. To duplicate the contents
+of an array use aclone().
+
Variant arrays can associate values with either strings or integers (32 or
+64 bit). Integer keys are not required to be consecutive. It should be noted that
+in the current implementation there is no difference in performance between these
+two methods. Both methods can be mixed in one array.
+
PTypes uses reference counting on arrays to properly clean up unused dynamic
+structures. However, since variant arrays may recursively contain arrays and in
+some situations there may be a circular reference, it is possible to have memory
+leak when using such structures. Since PTypes does not provide garbage collection
+(e.g. like in Java), compound variant data structures should be designed so that
+variants never reference each other circularly.
+
void aclear(variant& a) clears the variant array.
+This function may affect other variant objects holding a reference to the same
+array data.
+
variant aclone(const variant& a) creates a copy
+of an array or creates an empty array if a is of any
+other variant type. If a contains variant arrays as
+elements, only their references are copied.
+
void put(variant& a, <key>, const variant&
+item) associates item with key
+in the variant array a. Key
+can be either a string or an integer. The previous value associated with this
+key, if any, is replaced with the new value. If item
+is an unassigned variant, the new value is not stored in the array.
+
const variant& get(const variant& a, <key>)
+retrieves an element associated with key in the array
+a. If the element does not exist, or a
+is not an array, this function returns an unassigned variant (a reference to nullvar).
+Key can be either a string or an integer. Variant arrays
+use hashing to retrieve elements by keys.
+
const variant& variant::operator [](<key>)
+equivalent to get(). This operator can be used only
+for retrieving elements.
+
void del(variant& a, <key>) removes
+the element associated with key from the array a.
+Does nothing if the element does not exist or a is not
+an array. Equivalent to calling put() with an unassigned
+variant item.
+
bool anext(const variant& a, int& index, variant&
+item, [ string& key ]) iterates through array elements (please see
+examples in the introduction to variants). Each time anext()
+is called it assigns the next item in the array to item
+and optionally assigns its key value to key. Index
+must be zero when calling this function first time. If there are no items left
+in the array, or a is not an array, this function returns
+false.
The variant class offers a universal and flexible
+way to store values whose types can not be determined at compile time. A variant
+object can hold integer, boolean, floating point or string values as well as associative
+arrays of variants or references to objects derived from component.
+Variants can change their types at run-time. In return for their flexibility,
+variants have some memory usage and performance overhead. They always occupy 12
+bytes of static or local memory, however, they may also use dynamic memory allocation
+for strings and arrays.
+
The variant class provides all possible automatic
+typecasts, so that in most cases, when variants are used in assignments and function
+calls, the compiler will generate implicit typecasts appropriate for the context.
+The typecast functions always try to perform conversion regardless of the current
+and target types, although it may not always be sensible. For example, if a variant
+object holds an integer value, an attempt to cast it to a string will result in
+converting the value to its string representation; if a variant is an associative
+array, an attempt to cast it to boolean will return true if the array is not empty.
+Variant typecast routines never generate errors except when a 64-bit integer value
+is converted to 32-bit int and the value exceeds the allowed range.
+
A variant variable may itself represent an associative array of variants.
+
This interface can be useful for (but not limited to) designing interpreters
+and compilers, working with databases and spreadsheets, etc.
variant v1 = 11;
+variant v2 = "33";
+variant v3 = int(v1) + int(v2) + 22; // explicit typecasts: without them the compiler can't
+variant v4 = string(v2) + " cows"; // figure out which of + operators to use here
+large i1 = v1; // implicit typecast is possible here
+int a = v2; // large-to-int: may generate (evariant*) exception
+variant v5;
+put(v5, "key1", v1); // variants may be associated with both strings and
+put(v5, 15, "value2"); // integer values in variant arrays
+variant v6 = v5[15];
+variant v7 = v5; // a reference to the array is passed
+
+variant varray = aclone(v5); // array is duplicated
+
+variant item; // array iteration example
+for (int i = 0; anext(varray, i, item); )
+ pout.putf("%d: %d\n", i, int(item));
+Variants can hold references to objects derived from component.
+PTypes performs reference counting, so that assigning variants containing object
+references can be considered safe: an object will only be destroyed when there
+are no more references left to it. If you want to control the destruction of an
+object 'manually', you can increment the reference count with addref()
+before assigning a reference to a variant (see unknown
+& component). In this case, each call to addref()
+should be balanced with a call to release().
+
It should be noted that the reference counting mechanism has a potential flaw
+which may lead to memory leaks. Consider two objects containing variants with
+cross-referencing pointers to each other. The library can not keep track of such
+circular references, and an attempt to free one of the objects will result in
+destroying only one of them and leaving the other in the memory without any references
+to it. This is a known problem and can be solved by either eliminating circular
+references when designing data structures or by providing a complex memory 'garbage
+collector' which keeps track of all object references in the program.
Variants are compatible with variables and constants of all numeric types,
+the string type and a pointer to component, which means,
+you can assign any of these values to a variant object and vice versa, including
+passing as parameters to functions. The type of a variant can be changed at run-time
+by simply assigning a new value to it.
+
By default variants are initialized to an unassigned state, which is the same
+as calling clear() for a variant object.
+
You may store datetime values in variants, since
+datetime is equivalent to large
+in PTypes. Besides, arbitrary binary data can be stored in a dynamic string and
+then assigned to a variant.
+
The variant class declares (or rather, overloads) all possible typecast operators
+to other fundamental data types. These typecast operators always try to return
+some value, even when it is not sensible. Below is a list of rules for typecasts
+that may make sense in your program, leaving the other less meaningful cases as
+'unspecified'. Although variants store integers as 64-bit values, for simplicity
+we use the word int in this list.
+
+
integers and floating point variants are compatible and mutually castable
+in a manner similar to the C language rules
+
int to bool: returns true if the value is non-zero
+
int to string: returns the string representing the value in ASCII form
+
bool to int: returns 0 or 1
+
bool to string: returns "0" or "1"
+
string to int: converts the string to an integer; no spaces are allowed before
+or after the number; returns 0 if the string does not represent a number
+
string to float: similar to string-to-int, converts the string to a floating-point
+value
+
string to bool: returns true if the string is not empty
+
array to bool: returns true if the array is not empty
+
(component*) to bool: returns true if the pointer is not NULL
+
unassigned variants return 0, false, 0.0, empty string, empty array or NULL
+when trying to cast to int, boolean, floating point, string, array or reference,
+respectively.
+
+
Typecast operators never affect the actual value of a variant object.
+
When trying to cast a variant to signed or unsigned 32-bit int,
+the typecast operator first casts the value to 64-bit large
+according to the rules above, and then checks the resulting value. If the value
+is out of range, an exception of type (evariant*) is raised. You may always cast
+variants to large to get rid of extra exception handling
+code in your program.
Wshare is a simple multithreaded web server (HTTP
+daemon) that uses almost all functional parts of the library and serves as an
+example of using PTypes. Wshare supports protocol versions
+up to HTTP/1.1. It provides simple means of sharing files over the web, but it
+lacks server-side scripting and authentication functionality.
+
+
The program is covered by the same license as PTypes. The authors can not
+be responsible for any data loss or accidental disclosure of confidential information
+caused by this software. It is provided `as is' with the hope that it can be useful
+both as a library demo program and a simple web server.
+
+
The sources are in ./wshare. On UNIX, this application
+is built along with the library and is copied to ./bin.
+For MSVC, a separate project is provided as a part of the PTypes workspace. For
+BCC on Windows, the makefile is wshare/wshare.mak.
+
Wshare does not require any special installation
+or configuration procedures and can be easily run from the command line with at
+least one parameter: the directory you wish to share over the web. Note that wshare
+does not understand .htaccess or any other configuration
+files in your directories, so the web site you might previously have will run
+with wshare's default configuration and access rights.
+
Wshare is scalable: you can write your own handlers
+for new HTTP methods, virtual paths and for specific file extensions. Currently
+custom modules can be incorporated into wshare only
+at compile-time. Please, see wshare/modules.h and wshare/request.h
+for details. Also take a look at the sample compile-time module wshare/mod_about.cxx.
+
Please, run the program to see the summary of the command-line options.
+
+
+usage: wshare [options] document-root
+
+ -D daemonize, UNIX only
+ -d allow directory indexes
+ -g group group ID to run as, UNIX only
+ -n num maximum number of simultaneous connections (default: 30)
+ -o file-name write HTTP access log to file-name
+ -p port-num port number to listen to
+ -u user user ID to run as, UNIX only
+ -x always show directory indexes (ignore default index files)
+
+
+
+
+Running on UNIX
+
+
By default the daemon is trying to bind to port 80, which is a privileged port
+on UNIX. You will have to either run it as root, or to specify a port number higher
+than 1024 with the option -p port-number,
+e.g. -p 8080.
+
Wshare uses UNIX's syslog to log error messages.
+The HTTP access log goes to stderr, which can be overridden with -o logfile.
+
Wshare can also daemonize itself, i.e. detach from
+the console and remain active after the user logs out. Use option -D
+to daemonize the program. After that, the only way to stop the server is to send
+a SIGKILL signal, i.e. determine the process ID with ps
+and then invoke kill -KILL PID. On
+some systems you will need to use ps with the option
+that specifies your user ID.
+
For better security, wshare can downgrade process
+privileges to the user/group specified through -u user
+and -g group command-line options. This is done
+after binding to the socket and opening the log file. If -g
+is omitted and only -u is specified, wshare
+downgrades the process to the primary group of that user. For example, if you
+specify -u nobody, the group will be automatically assigned
+to a group nobody.
+
+
+Running on Windows
+
+
On Windows wshare runs as a simple console application.
+All you can do with wshare on Windows is to specify
+a different port to bind to with option -p port-number
+and to redirect the HTTP access log to some file with option -o logfile.
+Redirecting the access log to a file instead of writing it to the console window
+can speed up the server.
+
+
+Common features
+
+
By default wshare does not generate directory indexes
+(analogue of `Option Indexes' in Apache's configuration), which means the directory
+must have a default index file named as either of: index.html,
+Index.html or default.htm.
+Directory indexes can be allowed by specifying option -d.
+In this case, if the directory does not have any of the default index files listed
+above, wshare will generate a page with the list of
+files the directory contains.
+
If you don't want default index files at all, use option -x
+in the command line: the server will then show the directory indexes, like if
+you specified an empty "DirectoryIndex" directive with Apache.
+
You can limit the number of simultaneous connections with option -n
+num, which is 30 by default. When the number of connections reaches
+num/2 , the server stops supporting persistent connections.
+When it reaches num, the server sends "503 Service
+unavailable" to the client. And finally, when the number of connection requests
+reaches num * 2, wshare simply aborts the connection
+by closing the socket.
+
You can get the current status of the server by requesting http://localhost/.wstat
+in your browser. Wshare responds to this request only
+if the client is on the same host as the server, i.e. localhost
+or 127.0.0.1.
+
Some other features are hardcoded into wshare and
+can be changed only by recompiling it. The list of default index files can be
+found in wshare/config.cxx, and the file extension-to-MIME
+translation table is in wshare/mimetable.cxx. An awk
+script wshare/mimetable.awk is provided to translate
+an apache-style mime.conf file into C++ code that can
+be linked with wshare.
+
+
+Examples (UNIX)
+
+
The simplest usage:
+
+
wshare ./
+
+
Quickly share your files through port 8080 and ignore any index.html files
+in the directories:
+
+
wshare -x -p 8080 ~/mydocs
+
+
Run a real server for real users (must be root on UNIX):
The purpose of software engineering
+is to manage complexity, not to create it.
+Bill Catambay
+
+
PTypes (C++ Portable Types Library) is a simple alternative to the
+STL that includes multithreading and networking. It defines dynamic strings, variants,
+character sets, lists and other basic data types along with portable thread and
+synchronization objects, IP sockets and named pipes. Its main `target audience'
+is developers of complex network daemons, robots or non-visual client/server applications
+of any kind.
+
PTypes defines simple and intuitive interfaces and differs from the STL in
+fairly moderate use of templates. The library is portable across many modern operating
+systems (currently Linux, MacOS X, SunOS, FreeBSD, HP-UX and Windows). All platform-dependent
+issues are hidden inside. A simple web server called wshare is included in the
+package to demonstrate the full power of the library.
+
And finally, PTypes is open and free.
+
+
+
Documentation
+The documentation is available both on-line and off-line, i.e. as a part of the
+downloadable package. You may want to take a look at the Introduction
+first.
+
+
+
+
+
License
+PTypes is a free library and is covered by a simple license known as "zlib/libpng
+License". It grants full freedom to use the library for commercial, educational
+or any other purpose except that you can not change its name and/or the copyright
+notice and redistribute it as your own work.
Subscribe to new releases
+Become a freshmeat.net user and subscribe to the future announcements about PTypes.
+(freshmeat itself is a huge repository of open-source software.)
+
+
+
+
+
Discussion forum and bug
+tracking
+Post your questions, comments, feature requests and bug reports through PTypes
+project management page at SourceForge.net.