aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-06-16 10:26:47 -0400
committerDavid Robillard <d@drobilla.net>2019-01-05 17:12:38 +0100
commit0f943c203ae9653efabb8168b82d2e56898c5fac (patch)
treece32ff3f75525edfe02d08395350a44e0a93be75
parent6650e22960f4dcd7d66dc560aae0347dc3272e1d (diff)
downloadserd-0f943c203ae9653efabb8168b82d2e56898c5fac.tar.gz
serd-0f943c203ae9653efabb8168b82d2e56898c5fac.tar.bz2
serd-0f943c203ae9653efabb8168b82d2e56898c5fac.zip
WIP: Add C++ bindings
-rw-r--r--doc/reference.doxygen.in5
-rw-r--r--serd/serd.hpp1111
-rw-r--r--tests/serd_cxx_test.cpp381
-rw-r--r--wscript21
4 files changed, 1512 insertions, 6 deletions
diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in
index 2a02e35e..e54553d3 100644
--- a/doc/reference.doxygen.in
+++ b/doc/reference.doxygen.in
@@ -780,7 +780,8 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
-INPUT = @SERD_SRCDIR@/serd/serd.h
+INPUT = @SERD_SRCDIR@/serd/serd.h \
+ @SERD_SRCDIR@/serd/serd.hpp
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -847,7 +848,7 @@ EXCLUDE_PATTERNS =
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
-EXCLUDE_SYMBOLS =
+EXCLUDE_SYMBOLS = detail
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
diff --git a/serd/serd.hpp b/serd/serd.hpp
new file mode 100644
index 00000000..e7632212
--- /dev/null
+++ b/serd/serd.hpp
@@ -0,0 +1,1111 @@
+/*
+ Copyright 2018 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/// @file serd.hpp C++ API for Serd, a lightweight RDF syntax library
+
+#ifndef SERD_SERD_HPP
+#define SERD_SERD_HPP
+
+#include <serd/serd.h>
+
+#include <cassert>
+#include <memory>
+#include <ostream>
+#include <type_traits>
+#include <utility>
+
+/**
+ @defgroup serdxx Serdxx
+ C++ bindings for Serd, a lightweight RDF syntax library.
+ @{
+*/
+
+namespace serd {
+namespace detail {
+
+template <typename T>
+using FreeFunc = void (*)(T*);
+
+template <typename T>
+using CopyFunc = T* (*)(const T*);
+
+template <typename T>
+void
+no_free(T*)
+{
+}
+
+template <typename T>
+struct Deleter
+{
+ Deleter(FreeFunc<T> free_func) : _free_func(free_func) {}
+
+ void operator()(T* ptr)
+ {
+ if (_free_func) {
+ _free_func(ptr);
+ }
+ }
+
+ FreeFunc<T> _free_func;
+};
+
+/// C++ wrapper for a C object
+template <typename T, void FreeFunc(T*)>
+class Wrapper
+{
+public:
+ using CType = T;
+
+ Wrapper(T* ptr) : _ptr(ptr, Deleter<T>(FreeFunc)) {}
+
+ Wrapper(const T* ptr) : _ptr(const_cast<T*>(ptr), nullptr) {}
+
+ Wrapper(Wrapper&& wrapper) = default;
+ Wrapper& operator=(Wrapper&& wrapper) = default;
+
+ Wrapper(const Wrapper&) = delete;
+ Wrapper& operator=(const Wrapper&) = delete;
+
+ bool operator==(const Wrapper&) = delete;
+ bool operator!=(const Wrapper&) = delete;
+
+ operator const T*() const { return _ptr.get(); }
+
+ inline T* c_obj() { return _ptr.get(); }
+ inline const T* c_obj() const { return _ptr.get(); }
+
+protected:
+ Wrapper(std::nullptr_t) : _ptr(nullptr, nullptr) {}
+
+ std::unique_ptr<T, Deleter<T>> _ptr;
+};
+
+template <typename T, T* CopyFunc(const T*), void FreeFunc(T*)>
+class Copyable : public Wrapper<T, FreeFunc>
+{
+public:
+ Copyable(T* ptr) : Wrapper<T, FreeFunc>(ptr) {}
+
+ Copyable(const T* ptr) : Wrapper<T, FreeFunc>(ptr) {}
+
+ Copyable(const Copyable& wrapper)
+ : Wrapper<T, FreeFunc>(CopyFunc(wrapper.c_obj()))
+ {
+ }
+
+ Copyable(Copyable&&) = default;
+ Copyable& operator=(Copyable&&) = default;
+
+ Copyable& operator=(const Copyable& wrapper)
+ {
+ this->_ptr = std::unique_ptr<T, Deleter<T>>(CopyFunc(wrapper.c_obj()),
+ FreeFunc);
+ return *this;
+ }
+
+protected:
+ Copyable(std::nullptr_t) : Wrapper<T, FreeFunc>(nullptr) {}
+};
+
+static inline size_t
+stream_sink(const void* buf, size_t size, size_t nmemb, void* stream)
+{
+ try {
+ std::ostream* const s = static_cast<std::ostream*>(stream);
+ s->write(static_cast<const char*>(buf), std::streamsize(size * nmemb));
+ if (s->good()) {
+ return nmemb;
+ }
+ } catch (...) {
+ }
+ return 0;
+}
+
+/// Type-safe bit flags
+template <typename Flag>
+class Flags
+{
+public:
+ static_assert(std::is_enum<Flag>::value, "");
+
+ using FlagUnderlyingType = typename std::underlying_type<Flag>::type;
+ using Value = typename std::make_unsigned<FlagUnderlyingType>::type;
+
+ Flags() : _value(0) {}
+ Flags(const Flag f) : _value(static_cast<Value>(f)) {}
+ explicit Flags(const Value value) : _value{value} {}
+
+ Flags operator|(const Flag rhs) const
+ {
+ return Flags{_value | static_cast<Value>(rhs)};
+ }
+
+ Flags operator|(const Flags rhs) const
+ {
+ return Flags{_value | rhs._value};
+ }
+
+ operator Value() const { return _value; }
+
+private:
+ Value _value{};
+};
+
+} // namespace detail
+
+template <typename Flag>
+inline typename std::enable_if<std::is_enum<Flag>::value,
+ detail::Flags<Flag>>::type
+operator|(const Flag lhs, const Flag rhs)
+{
+ return detail::Flags<Flag>{lhs} | rhs;
+}
+
+/// Return status code
+enum class Status {
+ success = SERD_SUCCESS, ///< No error
+ failure = SERD_FAILURE, ///< Non-fatal failure
+ err_unknown = SERD_ERR_UNKNOWN, ///< Unknown error
+ err_bad_syntax = SERD_ERR_BAD_SYNTAX, ///< Invalid syntax
+ err_bad_arg = SERD_ERR_BAD_ARG, ///< Invalid argument
+ err_bad_iter = SERD_ERR_BAD_ITER, ///< Use of invalidated iterator
+ err_not_found = SERD_ERR_NOT_FOUND, ///< Not found
+ err_id_clash = SERD_ERR_ID_CLASH, ///< Clashing blank node IDs
+ err_bad_curie = SERD_ERR_BAD_CURIE, ///< Invalid CURIE
+ err_internal = SERD_ERR_INTERNAL, ///< Unexpected internal error
+ err_overflow = SERD_ERR_OVERFLOW, ///< Stack overflow
+ err_invalid = SERD_ERR_INVALID ///< Invalid data
+};
+
+/// RDF syntax type
+enum class Syntax {
+ Turtle = SERD_TURTLE, ///< Terse RDF Triple Language
+ NTriples = SERD_NTRIPLES, ///< Line-based RDF triples
+ NQuads = SERD_NQUADS, ///< Line-based RDF quads (NTriples with graphs)
+ TriG = SERD_TRIG ///< Terse RDF quads (Turtle with graphs)
+};
+
+/// Flags indicating inline abbreviation information for a statement
+enum class StatementFlag {
+ empty_S = SERD_EMPTY_S, ///< Empty blank node subject
+ anon_S = SERD_ANON_S, ///< Start of anonymous subject
+ anon_O = SERD_ANON_O, ///< Start of anonymous object
+ list_S = SERD_LIST_S, ///< Start of list subject
+ list_O = SERD_LIST_O ///< Start of list object
+};
+
+using StatementFlags = detail::Flags<StatementFlag>;
+
+/// Flags that control style for a model serialisation
+enum class SerialisationFlag {
+ no_inline_objects =
+ SERD_NO_INLINE_OBJECTS, ///< Do not inline objects where possible
+};
+
+using SerialisationFlags = detail::Flags<SerialisationFlag>;
+
+/**
+ Type of a syntactic RDF node
+
+ This is more precise than the type of an abstract RDF node. An abstract
+ node is either a resource, literal, or blank. In syntax there are two ways
+ to refer to a resource (by URI or CURIE) and two ways to refer to a blank
+ (by ID or anonymously). Anonymous (inline) blank nodes are expressed using
+ SerdStatementFlags rather than this type.
+*/
+enum class NodeType {
+ /**
+ The type of a nonexistent node
+
+ This type is useful as a sentinel, but is never emitted by the reader.
+ */
+ nothing = SERD_NOTHING,
+
+ /**
+ Literal value
+
+ A literal optionally has either a language, or a datatype (not both).
+ */
+ literal = SERD_LITERAL,
+
+ /**
+ URI (absolute or relative)
+
+ Value is an unquoted URI string, which is either a relative reference
+ with respect to the current base URI (e.g. "foo/bar"), or an absolute
+ URI (e.g. "http://example.org/foo").
+ @see <a href="http://tools.ietf.org/html/rfc3986">RFC3986</a>.
+ */
+ URI = SERD_URI,
+
+ /**
+ CURIE, a shortened URI
+
+ Value is an unquoted CURIE string relative to the current environment,
+ e.g. "rdf:type".
+ @see <a href="http://www.w3.org/TR/curie">CURIE Syntax 1.0</a>
+ */
+ CURIE = SERD_CURIE,
+
+ /**
+ A blank node
+
+ Value is a blank node ID, e.g. "id3", which is meaningful only within
+ this serialisation.
+ @see <a href="http://www.w3.org/TeamSubmission/turtle#nodeID">Turtle
+ <tt>nodeID</tt></a>
+ */
+ blank = SERD_BLANK
+};
+
+/// Flags indicating certain string properties relevant to serialisation
+enum class NodeFlag {
+ has_newline = SERD_HAS_NEWLINE, ///< Contains line breaks
+ has_quote = SERD_HAS_QUOTE, ///< Contains quotes
+ has_datatype = SERD_HAS_DATATYPE, ///< Literal node has datatype
+ has_language = SERD_HAS_LANGUAGE ///< Literal node has language
+};
+
+using NodeFlags = detail::Flags<NodeFlag>;
+
+/// Field in a statement
+enum class Field {
+ subject = SERD_SUBJECT, ///< Subject
+ predicate = SERD_PREDICATE, ///< Predicate ("key")
+ object = SERD_OBJECT, ///< Object ("value")
+ graph = SERD_GRAPH ///< Graph ("context")
+};
+
+/**
+ Syntax style options
+
+ The style of the writer output can be controlled by ORing together
+ values from this enumeration. Note that some options are only supported
+ for some syntaxes (e.g. NTriples does not support abbreviation and is
+ always ASCII).
+*/
+enum class WriterFlag {
+ ascii = SERD_STYLE_ASCII, ///< Escape all non-ASCII characters
+};
+
+using WriterFlags = detail::Flags<WriterFlag>;
+
+/// Indexing option
+enum class ModelFlag {
+ index_SPO = SERD_INDEX_SPO, ///< Subject, Predicate, Object
+ index_SOP = SERD_INDEX_SOP, ///< Subject, Object, Predicate
+ index_OPS = SERD_INDEX_OPS, ///< Object, Predicate, Subject
+ index_OSP = SERD_INDEX_OSP, ///< Object, Subject, Predicate
+ index_PSO = SERD_INDEX_PSO, ///< Predicate, Subject, Object
+ index_POS = SERD_INDEX_POS, ///< Predicate, Object, Subject
+ index_graphs = SERD_INDEX_GRAPHS ///< Support multiple graphs in model
+};
+
+using ModelFlags = detail::Flags<ModelFlag>;
+
+/**
+ A simple optional wrapper around a wrapped type with a pointer-like API
+
+ This works like a typical optional type, but only works with Wrapper types,
+ and exploits the fact that these are interally just pointers to avoid adding
+ space overhead for an "is_set" flag, like a generic optional class would.
+*/
+template <typename T>
+class Optional
+{
+public:
+ using CType = typename T::CType;
+
+ Optional() : _value() {}
+ Optional(std::nullptr_t) : _value() {}
+ Optional(const T& value) : _value(value) {}
+ Optional(T&& value) : _value(std::move(value)) {}
+
+ Optional(const Optional&) = default;
+ Optional(Optional&&) = default;
+ Optional& operator=(const Optional&) = default;
+ Optional& operator=(Optional&&) = default;
+
+ void reset() { *this = nullptr; }
+
+ const T& operator*() const
+ {
+ assert(_value.c_obj());
+ return _value;
+ }
+
+ T& operator*()
+ {
+ assert(_value.c_obj());
+ return _value;
+ }
+
+ const T* operator->() const
+ {
+ assert(_value.c_obj());
+ return &_value;
+ }
+
+ T* operator->()
+ {
+ assert(_value.c_obj());
+ return &_value;
+ }
+
+ bool operator==(const Optional& optional)
+ {
+ return (!*this && !optional) ||
+ (*this && optional && _value == optional._value);
+ }
+
+ bool operator!=(const Optional& optional) { return !operator==(optional); }
+
+ explicit operator bool() const { return _value.c_obj(); }
+
+ bool operator!() const { return !_value.c_obj(); }
+
+ inline CType* ptr() { return _value.c_obj(); }
+ inline const CType* ptr() const { return _value.c_obj(); }
+
+private:
+ T _value;
+};
+
+/**
+ A temporary reference to a string
+
+ This is used as a function parameter type to allow passing either
+ `std::string` or `const char*` without requiring multiple wrapper
+ definitions or the copying overhead of `std::string`.
+*/
+struct StringRef
+{
+ StringRef(const char* str) : _ptr(str) {}
+ StringRef(const std::string& str) : _ptr(str.c_str()) {}
+
+ StringRef(const StringRef&) = delete;
+ StringRef& operator=(const StringRef&) = delete;
+
+ operator const char*() const { return _ptr; }
+
+private:
+ const char* const _ptr;
+};
+
+/**
+ @name String Utilities
+ @{
+*/
+
+inline const char*
+strerror(const Status status)
+{
+ return serd_strerror(static_cast<SerdStatus>(status));
+}
+
+/**
+ @}
+ @name URI
+ @{
+*/
+
+inline std::string
+file_uri_parse(const StringRef& uri, std::string* hostname = nullptr)
+{
+ char* c_hostname = nullptr;
+ char* c_path = serd_file_uri_parse(uri, &c_hostname);
+ if (hostname && c_hostname) {
+ *hostname = c_hostname;
+ }
+ const std::string path(c_path);
+ free(c_hostname);
+ free(c_path);
+ return path;
+}
+
+/**
+ @}
+ @name Node
+ @{
+*/
+
+class Node : public detail::Copyable<SerdNode, serd_node_copy, serd_node_free>
+{
+public:
+ explicit Node(SerdNode* ptr) : Copyable(ptr) {}
+ explicit Node(const SerdNode* ptr) : Copyable(ptr) {}
+
+ NodeType type() const { return NodeType(serd_node_get_type(c_obj())); }
+ const char* c_str() const { return serd_node_get_string(c_obj()); }
+ std::string str() const { return c_str(); }
+ size_t size() const { return serd_node_get_length(c_obj()); }
+ size_t length() const { return serd_node_get_length(c_obj()); }
+ NodeFlags flags() const { return NodeFlags(serd_node_get_flags(c_obj())); }
+
+ Optional<Node> datatype() const
+ {
+ return Node(serd_node_get_datatype(c_obj()));
+ }
+
+ Optional<Node> language() const
+ {
+ return Node(serd_node_get_language(c_obj()));
+ }
+
+ Node resolve(const Node& base) const
+ {
+ return Node(serd_node_resolve(c_obj(), base.c_obj()));
+ }
+
+ bool operator==(const Node& node) const
+ {
+ return serd_node_equals(c_obj(), node.c_obj());
+ }
+
+ bool operator!=(const Node& node) const
+ {
+ return !serd_node_equals(c_obj(), node.c_obj());
+ }
+
+ bool operator<(const Node& node) const
+ {
+ return serd_node_compare(c_obj(), node.c_obj()) < 0;
+ }
+
+ operator std::string() const
+ {
+ return std::string(serd_node_get_string(c_obj()),
+ serd_node_get_length(c_obj()));
+ }
+
+private:
+ friend class Optional<Node>;
+ Node() : Copyable(nullptr) {}
+};
+
+inline std::ostream&
+operator<<(std::ostream& os, const Node& node)
+{
+ return os << node.c_str();
+}
+
+inline Node
+make_string(const StringRef& str)
+{
+ return Node(serd_new_string(str));
+}
+
+inline Node
+make_plain_literal(const StringRef& str, const StringRef& lang)
+{
+ return Node(serd_new_plain_literal(str, lang));
+}
+
+inline Node
+make_typed_literal(const StringRef& str, const Node& datatype)
+{
+ return Node(serd_new_typed_literal(str, datatype.c_obj()));
+}
+
+inline Node
+make_blank(const StringRef& str)
+{
+ return Node(serd_new_blank(str));
+}
+
+inline Node
+make_curie(const StringRef& str)
+{
+ return Node(serd_new_curie(str));
+}
+
+inline Node
+make_uri(const StringRef& str)
+{
+ return Node(serd_new_uri(str));
+}
+
+inline Node
+make_resolved_uri(const StringRef& str, const Node& base)
+{
+ return Node(serd_new_resolved_uri(str, base.c_obj()));
+}
+
+inline Node
+make_file_uri(const StringRef& path)
+{
+ return Node(serd_new_file_uri(path, nullptr));
+}
+
+inline Node
+make_file_uri(const StringRef& path, const StringRef& hostname)
+{
+ return Node(serd_new_file_uri(path, hostname));
+}
+
+inline Node
+make_relative_uri(const StringRef& str,
+ const Node& base,
+ const Optional<Node>& root = {})
+{
+ return Node(serd_new_relative_uri(str, base.c_obj(), root.ptr()));
+}
+
+inline Node
+make_decimal(double d,
+ unsigned frac_digits,
+ const Optional<Node>& datatype = {})
+{
+ return Node(serd_new_decimal(d, frac_digits, datatype.ptr()));
+}
+
+inline Node
+make_integer(int64_t i, const Optional<Node>& datatype = {})
+{
+ return Node(serd_new_integer(i, datatype.ptr()));
+}
+
+inline Node
+make_blob(const void* buf,
+ size_t size,
+ bool wrap_lines,
+ const Optional<Node>& datatype = {})
+{
+ return Node(serd_new_blob(buf, size, wrap_lines, datatype.ptr()));
+}
+
+/**
+ @}
+ @name World
+ @{
+*/
+
+class World : public detail::Wrapper<SerdWorld, serd_world_free>
+{
+public:
+ World() : Wrapper(serd_world_new()) {}
+
+ explicit World(SerdWorld* ptr) : Wrapper(ptr) {}
+
+ Node get_blank() { return Node(serd_world_get_blank(c_obj())); }
+
+ void set_message_sink(SerdMessageSink msg_sink, void* handle)
+ {
+ serd_world_set_message_sink(c_obj(), msg_sink, handle);
+ }
+};
+
+/**
+ @}
+ @name Statement
+ @{
+*/
+
+class Statement : public detail::Wrapper<SerdStatement, detail::no_free>
+{
+public:
+ Statement(const SerdStatement* statement) : Wrapper(statement) {}
+
+ Node node(Field field)
+ {
+ return Node(serd_statement_get_node(c_obj(),
+ static_cast<SerdField>(field)));
+ }
+
+ Node subject() const { return Node(serd_statement_get_subject(c_obj())); }
+
+ Node predicate() const
+ {
+ return Node(serd_statement_get_predicate(c_obj()));
+ }
+
+ Node object() const { return Node(serd_statement_get_object(c_obj())); }
+
+ Node graph() const { return Node(serd_statement_get_graph(c_obj())); }
+};
+
+/**
+ @}
+ @name Sink
+ @{
+*/
+
+class Sink
+{
+public:
+ Sink()
+ : _sink(serd_sink_new(this, NULL),
+ detail::Deleter<SerdSink>(serd_sink_free))
+ , _is_user(true)
+ {
+ serd_sink_set_base_func(_sink.get(), s_base);
+ serd_sink_set_prefix_func(_sink.get(), s_prefix);
+ serd_sink_set_statement_func(_sink.get(), s_statement);
+ serd_sink_set_end_func(_sink.get(), s_end);
+ }
+
+ explicit Sink(const SerdSink* ptr)
+ : _sink(const_cast<SerdSink*>(ptr), detail::Deleter<SerdSink>(nullptr))
+ , _is_user(false)
+ {
+ }
+
+ virtual ~Sink() = default;
+
+ virtual Status base(const Node& uri)
+ {
+ return _is_user ? Status::success
+ : Status(serd_sink_set_base(_sink.get(), uri.c_obj()));
+ }
+
+ virtual Status prefix(const Node& name, const Node& uri)
+ {
+ return _is_user ? Status::success
+ : Status(serd_sink_set_prefix(
+ _sink.get(), name.c_obj(), uri.c_obj()));
+ }
+
+ virtual Status statement(StatementFlags flags, const Statement& statement)
+ {
+ return _is_user ? Status::success
+ : Status(serd_sink_write_statement(
+ _sink.get(), flags, statement.c_obj()));
+ }
+
+ virtual Status write(StatementFlags flags,
+ const Node& subject,
+ const Node& predicate,
+ const Node& object,
+ const Optional<Node>& graph = {})
+ {
+ return _is_user ? Status::success
+ : Status(serd_sink_write(_sink.get(),
+ flags,
+ subject.c_obj(),
+ predicate.c_obj(),
+ object.c_obj(),
+ graph.ptr()));
+ }
+
+ virtual Status end(const Node& node)
+ {
+ return _is_user ? Status::success
+ : Status(serd_sink_end(_sink.get(), node.c_obj()));
+ }
+
+ const SerdSink* c_sink() const { return _sink.get(); }
+ SerdSink* c_sink() { return _sink.get(); }
+
+private:
+ static SerdStatus s_base(void* handle, const SerdNode* uri)
+ {
+ Sink* const sink = static_cast<Sink*>(handle);
+ return static_cast<SerdStatus>(sink->base(Node(uri)));
+ }
+
+ static SerdStatus
+ s_prefix(void* handle, const SerdNode* name, const SerdNode* uri)
+ {
+ Sink* const sink = static_cast<Sink*>(handle);
+ return static_cast<SerdStatus>(sink->prefix(Node(name), Node(uri)));
+ }
+
+ static SerdStatus s_statement(void* handle,
+ SerdStatementFlags flags,
+ const SerdStatement* statement)
+ {
+ Sink* const sink = static_cast<Sink*>(handle);
+ return static_cast<SerdStatus>(
+ sink->statement(StatementFlags(flags), statement));
+ }
+
+ static SerdStatus s_end(void* handle, const SerdNode* node)
+ {
+ Sink* const sink = static_cast<Sink*>(handle);
+ return static_cast<SerdStatus>(sink->end(Node(node)));
+ }
+
+ std::unique_ptr<SerdSink, detail::Deleter<SerdSink>> _sink;
+ bool _is_user;
+};
+
+/**
+ @}
+ @name Environment
+ @{
+*/
+
+class Env : public detail::Copyable<SerdEnv, serd_env_copy, serd_env_free>
+{
+public:
+ explicit Env(const Optional<Node>& base = {})
+ : Copyable(serd_env_new(base.ptr()))
+ {
+ }
+
+ /// Return the base URI
+ Node base_uri() const { return Node(serd_env_get_base_uri(c_obj())); }
+
+ /// Set the base URI
+ Status set_base_uri(const Node& uri)
+ {
+ return Status(serd_env_set_base_uri(c_obj(), uri.c_obj()));
+ }
+
+ /// Set a namespace prefix
+ Status set_prefix(const Node& name, const Node& uri)
+ {
+ return Status(serd_env_set_prefix(c_obj(), name.c_obj(), uri.c_obj()));
+ }
+
+ /// Set a namespace prefix
+ Status set_prefix(const StringRef& name, const StringRef& uri)
+ {
+ return Status(serd_env_set_prefix_from_strings(c_obj(), name, uri));
+ }
+
+ /// Qualify `uri` into a CURIE if possible
+ Optional<Node> qualify(const Node& uri) const
+ {
+ return Node(serd_env_qualify(c_obj(), uri.c_obj()));
+ }
+
+ /// Expand `node` into an absolute URI if possible
+ Optional<Node> expand(const Node& node) const
+ {
+ return Node(serd_env_expand(c_obj(), node.c_obj()));
+ }
+
+ /// Send all prefixes to `sink`
+ void send_prefixes(Sink& sink) const
+ {
+ serd_env_send_prefixes(c_obj(), sink.c_sink());
+ }
+
+ bool operator==(const Env& env)
+ {
+ return serd_env_equals(c_obj(), env.c_obj());
+ }
+
+ bool operator!=(const Env& env) { return !operator==(env); }
+};
+
+/**
+ @}
+ @name Reader
+ @{
+*/
+
+class Reader : public detail::Wrapper<SerdReader, serd_reader_free>
+{
+public:
+ Reader(World& world, Syntax syntax, const Sink& sink, size_t stack_size)
+ : Wrapper(serd_reader_new(world.c_obj(),
+ SerdSyntax(syntax),
+ sink.c_sink(),
+ stack_size))
+ {
+ }
+
+ void set_strict(bool strict) { serd_reader_set_strict(c_obj(), strict); }
+
+ void add_blank_prefix(const StringRef& prefix)
+ {
+ serd_reader_add_blank_prefix(c_obj(), prefix);
+ }
+
+ SerdStatus start_file(const StringRef& uri, bool bulk = true)
+ {
+ return serd_reader_start_file(c_obj(), uri, bulk);
+ }
+
+ SerdStatus start_stream(SerdReadFunc read_func,
+ SerdStreamErrorFunc error_func,
+ void* stream,
+ const Node& name,
+ size_t page_size)
+ {
+ return serd_reader_start_stream(c_obj(),
+ read_func,
+ error_func,
+ stream,
+ name.c_obj(),
+ page_size);
+ }
+
+ SerdStatus
+ start_string(const StringRef& utf8, const Optional<Node>& name = {})
+ {
+ return serd_reader_start_string(c_obj(), utf8, name.ptr());
+ }
+
+ SerdStatus read_chunk() { return serd_reader_read_chunk(c_obj()); }
+
+ SerdStatus read_document() { return serd_reader_read_document(c_obj()); }
+
+ SerdStatus finish() { return serd_reader_finish(c_obj()); }
+};
+
+/**
+ @}
+ @name Writer
+ @{
+*/
+
+class ByteSink : public detail::Wrapper<SerdByteSink, serd_byte_sink_free>
+{
+public:
+ ByteSink(SerdWriteFunc sink, void* stream, size_t block_size)
+ : Wrapper(serd_byte_sink_new(sink, stream, block_size))
+ {
+ }
+
+ ByteSink(std::ostream& stream)
+ : Wrapper(serd_byte_sink_new(detail::stream_sink, &stream, 1))
+ {
+ }
+};
+
+class Writer : public detail::Wrapper<SerdWriter, serd_writer_free>, public Sink
+{
+public:
+ Writer(World& world,
+ const Syntax syntax,
+ const WriterFlags writer_flags,
+ Env& env,
+ ByteSink& sink)
+ : Wrapper(serd_writer_new(
+ world.c_obj(),
+ SerdSyntax(syntax),
+ writer_flags,
+ env.c_obj(),
+ reinterpret_cast<SerdWriteFunc>(serd_byte_sink_write),
+ sink.c_obj()))
+ , Sink(serd_writer_get_sink(c_obj()))
+ {
+ }
+
+ Writer(World& world,
+ const Syntax syntax,
+ const WriterFlags writer_flags,
+ Env& env,
+ SerdWriteFunc write_func,
+ void* stream)
+ : Wrapper(serd_writer_new(world.c_obj(),
+ SerdSyntax(syntax),
+ writer_flags,
+ env.c_obj(),
+ write_func,
+ stream))
+ , Sink(serd_writer_get_sink(c_obj()))
+ {
+ }
+
+ Writer(World& world,
+ const Syntax syntax,
+ const WriterFlags writer_flags,
+ Env& env,
+ std::ostream& stream)
+ : Wrapper(serd_writer_new(world.c_obj(),
+ SerdSyntax(syntax),
+ writer_flags,
+ env.c_obj(),
+ detail::stream_sink,
+ &stream))
+ , Sink(serd_writer_get_sink(c_obj()))
+ {
+ }
+
+ Status set_root_uri(const Node& uri)
+ {
+ return Status(serd_writer_set_root_uri(c_obj(), uri.c_obj()));
+ }
+
+ SerdStatus finish() { return serd_writer_finish(c_obj()); }
+};
+
+/**
+ @}
+*/
+
+class Iter : public detail::Copyable<SerdIter, serd_iter_copy, serd_iter_free>
+{
+public:
+ Iter(SerdIter* i) : Copyable(i) {}
+ Iter(const SerdIter* i) : Copyable(i) {}
+
+ Iter& operator++()
+ {
+ serd_iter_next(c_obj());
+ return *this;
+ }
+
+ Statement operator*() const { return Statement(serd_iter_get(c_obj())); }
+
+ bool operator==(const Iter& i) const
+ {
+ return serd_iter_equals(c_obj(), i.c_obj());
+ }
+
+ bool operator!=(const Iter& i) const
+ {
+ return !serd_iter_equals(c_obj(), i.c_obj());
+ }
+};
+
+class Range
+ : public detail::Copyable<SerdRange, serd_range_copy, serd_range_free>
+{
+public:
+ Range(SerdRange* r) : Copyable(r) {}
+ Range(const SerdRange* r) : Copyable(r) {}
+
+ Iter begin() const { return Iter(serd_range_begin(c_obj())); }
+ Iter end() const { return Iter(serd_range_end(c_obj())); }
+
+ bool operator==(const Range& i) const
+ {
+ return serd_range_equals(c_obj(), i.c_obj());
+ }
+
+ bool operator!=(const Range& i) const
+ {
+ return !serd_range_equals(c_obj(), i.c_obj());
+ }
+};
+
+class Model
+ : public detail::Copyable<SerdModel, serd_model_copy, serd_model_free>
+{
+public:
+ using value_type = Statement;
+ using iterator = Iter;
+ using const_iterator = Iter;
+
+ Model(World& world, ModelFlags flags)
+ : Copyable(serd_model_new(world.c_obj(), flags))
+ {
+ }
+
+ Model(World& world, ModelFlag flag)
+ : Copyable(serd_model_new(world.c_obj(), ModelFlags(flag)))
+ {
+ }
+
+ size_t size() const { return serd_model_size(c_obj()); }
+
+ bool empty() const { return serd_model_empty(c_obj()); }
+
+ void insert(const Statement& s) { serd_model_insert(c_obj(), s.c_obj()); }
+
+ void insert(const Node& s,
+ const Node& p,
+ const Node& o,
+ const Optional<Node>& g = {})
+ {
+ serd_model_add(c_obj(), s.c_obj(), p.c_obj(), o.c_obj(), g.ptr());
+ }
+
+ Iter find(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {}) const
+ {
+ return Iter(
+ serd_model_find(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr()));
+ }
+
+ Range range(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {}) const
+ {
+ return Range(
+ serd_model_range(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr()));
+ }
+
+ Optional<Node> get(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {}) const
+ {
+ return Node(
+ serd_model_get(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr()));
+ }
+
+ Optional<Statement> get_statement(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {}) const
+ {
+ return Statement(serd_model_get_statement(
+ c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr()));
+ }
+
+ bool ask(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {}) const
+ {
+ return serd_model_ask(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr());
+ }
+
+ size_t count(const Optional<Node>& s,
+ const Optional<Node>& p,
+ const Optional<Node>& o,
+ const Optional<Node>& g = {}) const
+ {
+ return serd_model_count(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr());
+ }
+
+ bool operator==(const Model& model) const
+ {
+ return serd_model_equals(c_obj(), model.c_obj());
+ }
+
+ bool operator!=(const Model& model) const
+ {
+ return !serd_model_equals(c_obj(), model.c_obj());
+ }
+
+ Range all() const { return Range(serd_model_all(c_obj())); }
+
+ iterator begin() const { return iterator(serd_model_begin(c_obj())); }
+
+ iterator end() const { return iterator(serd_model_end(c_obj())); }
+};
+
+class Inserter : public detail::Wrapper<SerdInserter, serd_inserter_free>,
+ public Sink
+{
+public:
+ Inserter(Model& model, Env& env, const Optional<Node>& default_graph = {})
+ : Wrapper(serd_inserter_new(model.c_obj(),
+ env.c_obj(),
+ default_graph.ptr()))
+ , Sink(serd_inserter_get_sink(c_obj()))
+ {
+ }
+};
+
+} // namespace serd
+
+/**
+ @}
+*/
+
+#endif /* SERD_SERD_HPP */
diff --git a/tests/serd_cxx_test.cpp b/tests/serd_cxx_test.cpp
new file mode 100644
index 00000000..2196df56
--- /dev/null
+++ b/tests/serd_cxx_test.cpp
@@ -0,0 +1,381 @@
+/*
+ Copyright 2018 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#undef NDEBUG
+
+#include "serd/serd.hpp"
+
+#include <cassert>
+#include <iostream>
+#include <sstream>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+template <typename T>
+static int
+test_move_only(T&& obj)
+{
+ static_assert(!std::is_copy_constructible<T>::value, "");
+ static_assert(!std::is_copy_assignable<T>::value, "");
+
+ const auto* const ptr = obj.c_obj();
+
+ // Move construct
+ T moved{std::move(obj)};
+ assert(moved.c_obj() == ptr);
+ assert(!obj.c_obj());
+
+ // Move assign
+ obj = std::move(moved);
+ assert(obj.c_obj() == ptr);
+ assert(!moved.c_obj());
+
+ return 0;
+}
+
+template <typename T>
+static int
+test_copy_move(const T& obj)
+{
+ T copy{obj};
+ assert(copy == obj);
+
+ T moved{std::move(copy)};
+ assert(moved == obj);
+ assert(copy != obj);
+
+ T copy_assigned{obj};
+ copy_assigned = obj;
+ assert(copy_assigned == obj);
+
+ T move_assigned{obj};
+ move_assigned = std::move(copy_assigned);
+ assert(move_assigned == obj);
+ assert(copy_assigned != obj);
+
+ return 0;
+}
+
+static int
+test_operators()
+{
+ int st = 0;
+
+ serd::World world;
+
+ serd::Model model(world, serd::ModelFlag::index_SPO);
+ model.insert(serd::make_uri("http://example.org/s"),
+ serd::make_uri("http://example.org/p"),
+ serd::make_uri("http://example.org/o"));
+
+ serd::Sink sink;
+ serd::Env env;
+
+ std::ostringstream stream;
+ serd::ByteSink byte_sink{stream};
+
+ st |= test_copy_move(
+ serd::Optional<serd::Node>{serd::make_string("hello")});
+ st |= test_move_only(serd::World{});
+ st |= test_move_only(*model.begin());
+ st |= test_copy_move(serd::Env{});
+ st |= test_move_only(serd::Reader{world, serd::Syntax::Turtle, sink, 4096});
+ st |= test_move_only(serd::ByteSink{stream});
+ st |= test_copy_move(model.begin());
+ st |= test_copy_move(model.all());
+ // Sink
+ st |= test_copy_move(model);
+ // st |= test_move_only(serd::Inserter{model, env});
+ // st |= test_move_only(serd::Sink{});
+
+ st |= test_copy_move(serd::Env{});
+
+ return st;
+}
+
+static int
+test_optional()
+{
+ test_copy_move(serd::Optional<serd::Node>(serd::make_string("node")));
+
+ const serd::Node node = serd::make_string("node");
+ const serd::Node other = serd::make_string("other");
+
+ // Truthiness
+ assert(!serd::Optional<serd::Node>());
+ assert(!serd::Optional<serd::Node>(nullptr));
+ assert(serd::Optional<serd::Node>(node));
+
+ // Comparison and general sanity
+ serd::Optional<serd::Node> optional{node};
+ assert(optional);
+ assert(optional == node);
+ assert(optional != other);
+ assert(*optional == node);
+ assert(optional->str() == "node");
+ assert(optional.ptr() != node.c_obj()); // non-const, must be a copy
+
+ // Reset
+ optional.reset();
+ assert(!optional);
+ assert(!optional.ptr());
+
+ // Copying and moving
+ serd::Node nonconst = serd::make_string("nonconst");
+ const SerdNode* c_node = nonconst.c_obj();
+
+ optional = nonconst;
+ serd::Optional<serd::Node> copied{optional};
+ assert(copied == nonconst);
+ assert(copied.ptr() != c_node);
+
+ optional = std::move(nonconst);
+ serd::Optional<serd::Node> moved{std::move(optional)};
+ assert(moved.ptr() == c_node);
+ assert(!optional);
+
+ serd::Optional<serd::Node> copy_assigned;
+ copy_assigned = optional;
+ assert(copy_assigned == optional);
+ assert(copy_assigned.ptr() != c_node);
+
+ serd::Optional<serd::Node> move_assigned;
+ move_assigned = std::move(moved);
+ assert(move_assigned.ptr() == c_node);
+ assert(!optional);
+
+ return 0;
+}
+
+static int
+test_node(const serd::Node& node)
+{
+ test_copy_move(node);
+
+ if (node.datatype()) {
+ return test_node(*node.datatype());
+ } else if (node.language()) {
+ return test_node(*node.language());
+ }
+ return 0;
+}
+
+static int
+test_nodes()
+{
+ using namespace serd;
+
+ const auto type = make_uri("http://example.org/Type");
+ const auto base = make_uri("http://example.org/");
+ const auto root = make_uri("http://example.org/");
+
+ assert(!test_node(make_string("hello")));
+ assert(!test_node(make_plain_literal("hello", "en")));
+ assert(!test_node(make_typed_literal("hello", type)));
+ assert(!test_node(make_blank("blank")));
+ assert(!test_node(make_curie("eg:curie")));
+ assert(!test_node(make_uri("http://example.org/thing")));
+ assert(!test_node(make_resolved_uri("thing", base)));
+ assert(!test_node(make_file_uri("/foo/bar", "host")));
+ assert(!test_node(make_file_uri("/foo/bar")));
+ assert(!test_node(make_file_uri("/foo/bar", "host")));
+ assert(!test_node(make_file_uri("/foo/bar")));
+ assert(!test_node(make_relative_uri("http://example.org/a", base)));
+ assert(!test_node(make_relative_uri("http://example.org/a", base, root)));
+ assert(!test_node(make_decimal(1.2, 7)));
+ assert(!test_node(make_decimal(3.4, 7, type)));
+ assert(!test_node(make_integer(56)));
+ assert(!test_node(make_integer(78, type)));
+ assert(!test_node(make_blob("blob", 4, true)));
+ assert(!test_node(make_blob("blob", 4, true, type)));
+
+ return 0;
+}
+
+static int
+test_reader()
+{
+ struct Sink : public serd::Sink
+ {
+ serd::Status statement(serd::StatementFlags,
+ const serd::Statement& statement) override
+ {
+ ++n_statements;
+ stream << statement.subject() << " " << statement.predicate() << " "
+ << statement.object() << std::endl;
+ return serd::Status::success;
+ }
+
+ size_t n_statements{};
+ std::stringstream stream{};
+ };
+
+ Sink sink;
+ serd::World world;
+ serd::Reader reader(world, serd::Syntax::Turtle, sink, 4096);
+
+ reader.start_string("@prefix eg: <http://example.org> ."
+ "eg:s eg:p eg:o1 , eg:o2 .");
+ reader.read_document();
+
+ assert(sink.n_statements == 2);
+ assert(sink.stream.str() == "eg:s eg:p eg:o1\neg:s eg:p eg:o2\n");
+
+ return 0;
+}
+
+static int
+test_writer()
+{
+ serd::World world;
+ serd::Env env;
+ std::ostringstream stream;
+ serd::ByteSink sink(stream);
+ serd::Writer writer(world, serd::Syntax::Turtle, {}, env, stream);
+
+ writer.base(serd::make_uri("http://drobilla.net/base/"));
+ writer.set_root_uri(serd::make_uri("http://drobilla.net/"));
+ writer.prefix(serd::make_string("eg"),
+ serd::make_uri("http://example.org/"));
+ writer.write({},
+ serd::make_uri("http://drobilla.net/base/s"),
+ serd::make_uri("http://example.org/p"),
+ serd::make_uri("http://drobilla.net/o"));
+
+ writer.finish();
+
+ assert(stream.str() == "@base <http://drobilla.net/base/> .\n"
+ "@prefix eg: <http://example.org/> .\n"
+ "\n"
+ "<s>\n"
+ "\teg:p <../o> .\n");
+
+ return 0;
+}
+
+static int
+test_env()
+{
+ serd::Env env;
+
+ const auto base = serd::make_uri("http://drobilla.net/");
+ env.set_base_uri(base);
+ assert(env.base_uri() == base);
+
+ env.set_prefix(serd::make_string("eg"),
+ serd::make_uri("http://example.org/"));
+
+ assert(env.qualify(serd::make_uri("http://example.org/foo")) ==
+ serd::make_curie("eg:foo"));
+
+ assert(env.expand(serd::make_uri("foo")) ==
+ serd::make_uri("http://drobilla.net/foo"));
+
+ serd::Env copied{env};
+ assert(copied.qualify(serd::make_uri("http://example.org/foo")) ==
+ serd::make_curie("eg:foo"));
+
+ assert(copied.expand(serd::make_uri("foo")) ==
+ serd::make_uri("http://drobilla.net/foo"));
+
+ serd::Env assigned;
+ assigned = env;
+ auto curie = env.qualify(serd::make_uri("http://example.org/foo"));
+
+ assert(assigned.qualify(serd::make_uri("http://example.org/foo")) ==
+ serd::make_curie("eg:foo"));
+
+ assert(assigned.expand(serd::make_uri("foo")) ==
+ serd::make_uri("http://drobilla.net/foo"));
+
+ return 0;
+}
+
+static int
+test_model()
+{
+ serd::World world;
+ serd::Model model(world,
+ serd::ModelFlag::index_SPO | serd::ModelFlag::index_OPS);
+
+ assert(model.empty());
+
+ const auto s = serd::make_uri("http://example.org/s");
+ const auto p = serd::make_uri("http://example.org/p");
+ const auto o1 = serd::make_uri("http://example.org/o1");
+ const auto o2 = serd::make_uri("http://example.org/o2");
+
+ model.insert(s, p, o1);
+ model.insert(s, p, o2);
+
+ assert(!model.empty());
+ assert(model.size() == 2);
+ assert(model.ask(s, p, o1));
+ assert(model.ask(s, p, o1));
+ assert(!model.ask(s, p, s));
+
+ size_t total_count = 0;
+ for (const auto& statement : model) {
+ assert(statement.subject() == s);
+ assert(statement.predicate() == p);
+ assert(statement.object() == o1 || statement.object() == o2);
+ ++total_count;
+ }
+ assert(total_count == 2);
+
+ size_t o1_count = 0;
+ for (const auto& statement : model.range({}, {}, o1)) {
+ assert(statement.subject() == s);
+ assert(statement.predicate() == p);
+ assert(statement.object() == o1);
+ ++o1_count;
+ }
+ assert(o1_count == 1);
+
+ size_t o2_count = 0;
+ for (const auto& statement : model.range({}, {}, o2)) {
+ assert(statement.subject() == s);
+ assert(statement.predicate() == p);
+ assert(statement.object() == o2);
+ ++o2_count;
+ }
+ assert(o2_count == 1);
+
+ serd::Model copy(model);
+ assert(copy == model);
+
+ copy.insert(s, p, s);
+ assert(copy != model);
+
+ return 0;
+}
+
+int
+main()
+{
+ int st = 0;
+
+ st |= test_operators();
+ st |= test_optional();
+ st |= test_nodes();
+ st |= test_env();
+ st |= test_reader();
+ st |= test_writer();
+ st |= test_model();
+
+ return st;
+}
diff --git a/wscript b/wscript
index 6346c9da..b1275535 100644
--- a/wscript
+++ b/wscript
@@ -21,6 +21,7 @@ out = 'build' # Build directory
def options(ctx):
ctx.load('compiler_c')
+ ctx.load('compiler_cxx')
autowaf.set_options(ctx, test=True)
opt = ctx.get_option_group('Configuration options')
autowaf.add_flags(
@@ -39,8 +40,10 @@ def options(ctx):
def configure(conf):
autowaf.display_header('Serd Configuration')
conf.load('compiler_c', cache=True)
+ conf.load('compiler_cxx', cache=True)
conf.load('autowaf', cache=True)
autowaf.set_c_lang(conf, 'c99')
+ autowaf.set_cxx_lang(conf, 'c++11')
conf.env.update({
'BUILD_UTILS': not Options.options.no_utils,
@@ -127,7 +130,7 @@ lib_source = ['src/base64.c',
def build(bld):
# C Headers
includedir = '${INCLUDEDIR}/serd-%s/serd' % SERD_MAJOR_VERSION
- bld.install_files(includedir, bld.path.ant_glob('serd/*.h'))
+ bld.install_files(includedir, bld.path.ant_glob('serd/*.h*'))
# Pkgconfig file
autowaf.build_pc(bld, 'SERD', SERD_VERSION, SERD_MAJOR_VERSION, [],
@@ -167,6 +170,7 @@ def build(bld):
if bld.env.BUILD_TESTS:
test_args = {'includes': ['.', './src'],
'cflags': [''] if bld.env.NO_COVERAGE else ['--coverage'],
+ 'cxxflags': [''] if bld.env.NO_COVERAGE else ['--coverage', '-fno-inline'],
'linkflags': [''] if bld.env.NO_COVERAGE else ['--coverage'],
'lib': lib_args['lib'],
'install_path': ''}
@@ -193,6 +197,13 @@ def build(bld):
defines = defines,
**test_args)
+ bld(features = 'cxx cxxprogram',
+ source = 'tests/serd_cxx_test.cpp',
+ use = 'libserd_profiled',
+ target = 'serd_cxx_test',
+ defines = defines,
+ **test_args)
+
# Utilities
if bld.env.BUILD_UTILS:
for i in ['serdi', 'serd_validate']:
@@ -535,14 +546,16 @@ def test(ctx):
srcdir = ctx.path.abspath()
os.environ['PATH'] = '.' + os.pathsep + os.getenv('PATH')
- autowaf.pre_test(ctx, APPNAME)
+ dirs = ['.', 'src', 'tests']
+ autowaf.pre_test(ctx, APPNAME, dirs=dirs)
autowaf.run_tests(ctx, APPNAME,
['serd_test',
+ 'serd_cxx_test',
'read_chunk_test',
'nodes_test',
'overflow_test',
'model_test'],
- name='Unit')
+ dirs=dirs, name='Unit')
def test_syntax_io(in_name, expected_name, lang):
in_path = 'tests/good/%s' % in_name
@@ -608,7 +621,7 @@ def test(ctx):
for opts in ['', '-m']:
run_test_suites(ctx, opts)
- autowaf.post_test(ctx, APPNAME)
+ autowaf.post_test(ctx, APPNAME, dirs=dirs)
def posts(ctx):
path = str(ctx.path.abspath())