Next: The C API, Previous: Introduction, Up: Top [Contents][Index]
The C++ API provides users an easy to use, object-oriented interface to a wide range of file monitoring APIs. This API provides a common facade to a set of heterogeneous APIs that not only greatly simplifies their usage, but provides an indirection layer that makes applications more portable: as far as there is an available monitor in another platform, an existing application will just work.
In reality, a monitor may have platform-specific behaviours that should be taken into account when writing portable applications using this library. This differences complicate the task of writing portable applications that are truly independent of the file monitoring API they may be using. However, monitors try to ‘compensate’ for any behavioural difference across implementations.
The typical usage pattern of this API is similar to the following:
monitor
is either created directly or through
the factory (see Monitor Discovery).
Since multiple monitor implementations
exist and the caller potentially ignores which monitors will be
available at run time, there must exist a way to query the
API for the list of available monitor and request a
particular instance. The monitor_factory
is an object factory
class that provides basic monitor registration and
discovery functionality: API clients can query the
monitor registry to get a list of available monitors and get an
instance of a monitor either by type or by name.
The monitor_factory
class provides the following methods:
static monitor * create_monitor
Creates a monitor of the specified type with the specified constructor
parameters (see Monitors). A monitor of the platform default type
can be created if
fsw_monitor_type::system_default_monitor_type
. If the named
monitor is not available, then return nullptr
.
static monitor * create_monitor_by_name
Creates a monitor of the specified type (by name) and
constructor parameters (see Monitors). If the named monitor is
not available, then return nullptr
.
static std::vector<std::string> get_types()
Get the list of available monitor types. The type name can then be
used to get a monitor instance by name using
create_monitor_by_name
.
static bool exists_type(const std::string& name)
Query whether the specified monitor exists.
static void register_type(const std::string& name, fsw_monitor_type type)
Register a monitor type in the list of available implementations.
In order for monitor types to be visible to the factory they have to be registered. Currently they can be registered using two helper macros, defined in monitor.h:
REGISTER_MONITOR(classname, monitor_type)
This macro must be invoked into a class’ header file and must be passed the class name and the monitor type.
REGISTER_MONITOR_IMPL(classname, monitor_type)
This macro must be invoked into a class’ source file and must be passed the class name and the monitor type.
The same monitor type cannot be used to register multiple monitor implementations. No checks are in place to detect this situation and the registration will succeed; however, the registration process of multiple monitor implementations for the same monitor type is not deterministic.
The monitor
class is the fundamental type of
the C++ API: it defines the interface of every monitor and
provides common functionality to inheritors of this class.
The public interface of a monitor is the following:
class monitor { public: monitor(std::vector<std::string> paths, FSW_EVENT_CALLBACK * callback, void * context = nullptr); virtual ~monitor(); monitor(const monitor& orig) = delete; monitor& operator=(const monitor & that) = delete; void set_properties( std::map<std::string, std::string> options); std::string get_property(std::string name); void set_latency(double latency); void set_allow_overflow(bool overflow); void set_recursive(bool recursive); void set_directory_only(bool directory_only); void add_filter(const monitor_filter &filter); void set_filters( const std::vector<monitor_filter> &filters); void set_follow_symlinks(bool follow); void * get_context() const; void set_context(void * context); void start(); void add_event_type_filter( const fsw_event_type_filter &filter); void set_event_type_filters( const std::vector<fsw_event_type_filter> &filters); }
A monitor is thus a type with the following characteristics:
The business interface of the monitor
class is the following:
void set_properties(std::map<std::string, std::string> options);
This function sets a map of monitor-specific properties.
std::string get_property(std::string name);
This function returns the property named name
.
void set_allow_overflow(bool allow_overflow);
This function sets the allow overflow flag to the specified value. If
this flag is set, then the monitor will report a monitor buffer
overflow as a change event of type fsw_event_flag::Overflow
.
void set_latency(double latency);
This function sets the latency of the monitor in seconds. This method only sets the latency value. The exact meaning of latency and how it is enforced depends on a monitor implementation.
void set_recursive(bool recursive);
This function sets the recursive
flag of the monitor to
indicate whether the monitor should recursively observe the contents
of directories.
void set_directory_only(bool directory_only);
This function sets the directory only flag to the specified value. If this flag is set, then the monitor will only watch directories during a recursive scan. This functionality is only supported by monitors whose backend fires change events on a directory when one its children is changed. If a monitor backend does not support this functionality, the flag is ignored.
void add_filter(const monitor_filter &filter);
This function adds a monitor_filter
instance to the filter list
of the current monitor.
void set_filters(const std::vector<monitor_filter> &filters);
This function sets the filter list of the current monitor, substituting existing filter if any.
void set_follow_symlinks(bool follow);
This function sets the follow_symlinks
flag of the monitor to
indicate whether the monitor should follow observed symbolic links or
observe the links themselves.
void * get_context() const;
This function gest the pointer to the context data that is passed to the callback by the monitor.
void set_context(void * context);
This function sets the pointer to the context data that is passed to the callback by the monitor.
void start();
This function starts the monitor so that it begins listening to file system change events.
void add_event_type_filter(const fsw_event_type_filter &filter);
This function adds a fsw_event_type_filter
instance to the
event type filter list of the current monitor.
void set_event_type_filters(const std::vector<fsw_event_type_filter> &filters);
This function sets the event type filter list of the current monitor, substituting existing filters if any.
monitor
is a class that declares the following protected
functions:
bool accept_event_type(fsw_event_flag event_type) const
This function checks whether the specified event_type
can be
accepted according to the list of event type filters of the monitor.
bool accept_path(const std::string &path) const
This function checks whether the specified path
can be accepted
according to the list of filters of the monitor.
bool accept_path(const char *path) const
This function checks whether the specified path
can be accepted
according to the list of filters of the monitor.
void notify_events(const std::vector<event> &events) const
This function notifies that detection of the specified events
.
void notify_overflow(const std::string & path) const
This function notifies that the monitor has overflowed. Overflowing is a monitor-specific concept and not all monitors experience this behaviour.
std::vector<fsw_event_flag> filter_flags(const event &evt) const
This function filters the list of flags of an event evt
using
the list of event type filters of the monitor. If no filters are set,
all the flags are returned.
virtual void run() = 0
This pure virtual function shall contain the logic of a monitor
implementation. This function will be invoked by the monitor’s
start
API function.
Since it contains a pure virtual function, run()
, the
monitor
class is abstract. Inheritors are required to provide
an implementation of the run()
function containing the monitor
logic and its ‘event loop’.
The anatomy of monitors is typically very similar and it can be illustrated with the following algorithm (written in pseudo-code):
void run() { initialize_api(); while (true) { scan_paths(); wait_for_events(latency); vector<change_events> evts = get_changes(); vector<event> events; for (auto & evt : evts) { if (accept(evt.get_path)) { events.push_back({event from evt}); } } if (events.size()) { notify_events(events); } } }
Despite being a minimal implementation, this algorithm exemplifies the common tasks performed by a monitor:
notify_events
method is called on the base class to filter
the events and notify the caller.
Events are modeled by the fsw::event
class,
defined in the event.h
header:
class event { public: event(std::string path, time_t evt_time, std::vector<fsw_event_flag> flags); virtual ~event(); std::string get_path() const; time_t get_time() const; std::vector<fsw_event_flag> get_flags() const; static fsw_event_flag get_event_flag_by_name(const std::string &name); static std::string get_event_flag_name(const fsw_event_flag &flag); private: std::string path; time_t evt_time; std::vector<fsw_event_flag> evt_flags; };
The event
class provides a simple and uniform representation of
an event to all the API. An event has got the following
characteristics:
Currently the API provides no way for monitor implementors
to provide additional, monitor-dependent fields to an event. Since
the API stores events by value into collections (such as
vector
), an extended event would be sliced and
additional fields would be lost.
Event types can be looked up by name using the following function:
fsw_event_flag get_event_flag_by_name(const std::string &name);
Returns the fsw_event_flag
instance whose name is name.
If no instance is found with the specified name, this function
will throw a libfsw_exception
.
The name of an event type can be obtained using the following function:
static std::string get_event_flag_name(const fsw_event_flag &flag);
This function returns the name of the specified event type.
Most of the times, the name of an event type is used when writing user output: to ease this task, the event.h header defines the following operator overload:
ostream& operator<<(ostream& out, const fsw_event_flag flag);
This operator writes the name of the specified fsw_event_flag
to the stream.
Path filters are regular expression used to
accept or reject file change events based on the value of their path.
A filter is represented by the fsw::monitor_filter
type,
defined in the filter.h
header:
typedef struct monitor_filter { std::string text; fsw_filter_type type; bool case_sensitive; bool extended; } monitor_filter;
and has the following characteristics:
text
The regular expression used to match paths.
type
The filter type can either be an inclusion or exclusion filter.
case_sensitive
A flag indicating the filter case sensitivitiy.
extended
A flag indicating whether text
is an extended regular
expression.
A filter type determine whether the filter regular expression is used to include and exclude paths from the list of the events processed by the library. libfswatch processes filters this way:
Said another way:
The fswatch
Info documentation has a user-oriented
discussion of how filters are used.
Event type filters let callers
filter the events using a specified set of event types. An event type
filter is represented by the fsw_event_type_filter
type,
defined in the cfilter.h
header:
typedef struct fsw_event_type_filter { fsw_event_flag flag; } fsw_event_type_filter;
Next: The C API, Previous: Introduction, Up: Top [Contents][Index]