From a70ee297d71d03c004e78aa1061877f231cfd8d2 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 16 Jun 2018 10:26:47 -0400 Subject: WIP: Add C++ bindings --- serd/serd.hpp | 1029 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1029 insertions(+) create mode 100644 serd/serd.hpp (limited to 'serd') diff --git a/serd/serd.hpp b/serd/serd.hpp new file mode 100644 index 00000000..bb2d48a1 --- /dev/null +++ b/serd/serd.hpp @@ -0,0 +1,1029 @@ +/* + Copyright 2018 David Robillard + + 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 + +#include +#include +#include +#include +#include + +/** + @defgroup serdxx Serdxx + C++ bindings for Serd, a lightweight RDF syntax library. + @{ +*/ + +namespace serd { + +class Node; + +/** + Return status code. +*/ +enum class Status { + Success = SERD_SUCCESS, /**< No error */ + Failure = SERD_FAILURE, /**< Non-fatal failure */ + ErrUnknown = SERD_ERR_UNKNOWN, /**< Unknown error */ + ErrBadSyntax = SERD_ERR_BAD_SYNTAX, /**< Invalid syntax */ + ErrBadArg = SERD_ERR_BAD_ARG, /**< Invalid argument */ + ErrNotFound = SERD_ERR_NOT_FOUND, /**< Not found */ + ErrIdClash = SERD_ERR_ID_CLASH, /**< Clashing blank node IDs */ + ErrBadCurie = SERD_ERR_BAD_CURIE, /**< Invalid CURIE */ + ErrInternal = SERD_ERR_INTERNAL /**< Unexpected internal error */ +}; + +namespace detail { + +template +using FreeFunc = void (*)(T*); + +template +using CopyFunc = T* (*)(const T*); + +template +void +no_free(T*) +{ +} + +template +struct Deleter +{ + Deleter(FreeFunc free_func) : _free_func(free_func) {} + + void operator()(T* ptr) + { + if (_free_func) { + _free_func(ptr); + } + } + + FreeFunc _free_func; +}; + +/** C++ wrapper for a C object. */ +template +class Wrapper +{ +public: + using CType = T; + + Wrapper(T* ptr) : _ptr(ptr, Deleter(FreeFunc)) {} + + Wrapper(const T* ptr) : _ptr(const_cast(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> _ptr; +}; + +template +class Copyable : public Wrapper +{ +public: + Copyable(T* ptr) : Wrapper(ptr) {} + + Copyable(const T* ptr) : Wrapper(ptr) {} + + Copyable(const Copyable& wrapper) + : Wrapper(CopyFunc(wrapper.c_obj())) + { + } + + Copyable(Copyable&&) = default; + Copyable& operator=(Copyable&&) = default; + + Copyable& operator=(const Copyable& wrapper) + { + this->_ptr = std::unique_ptr>(CopyFunc(wrapper.c_obj()), + FreeFunc); + return *this; + } + +protected: + Copyable(std::nullptr_t) : Wrapper(nullptr) {} +}; + +template +size_t +stream_sink(const void* buf, size_t size, size_t nmemb, void* stream) +{ + using Stream = std::basic_ostream; + + try { + Stream* const s = static_cast(stream); + s->write(static_cast(buf), std::streamsize(size * nmemb)); + if (s->good()) { + return nmemb; + } + } catch (...) { + } + return 0; +} + +} // namespace detail + +/** + 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. + The fact that these are interally just pointers is used to avoid adding + space overhead for an "is_set" flag, like a generic optional class would. +*/ +template +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; +}; + +using Type = SerdType; +using NodeFlags = SerdNodeFlags; +using StyleFlags = SerdStyleFlags; +using IndexOptions = SerdIndexOptions; +using Field = SerdField; +using StatementFlags = SerdStatementFlags; + +class Statement; + +/** + RDF syntax. +*/ +enum class Syntax { + /** + Turtle - Terse RDF Triple Language (UTF-8). + @see Turtle + */ + Turtle = SERD_TURTLE, + + /** + NTriples - Line-based RDF triples (ASCII). + @see NTriples + */ + NTriples = SERD_NTRIPLES, + + /** + NQuads - Line-based RDF quads (UTF-8). + @see NQuads + */ + NQuads = SERD_NQUADS, + + /** + TriG - Terse RDF quads (UTF-8). + @see Trig + */ + TriG = SERD_TRIG +}; + +/** + 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 Style { + ascii = SERD_STYLE_ASCII, /**< Escape all non-ASCII characters. */ +}; + +inline StyleFlags +operator|(const Style a, const Style b) +{ + return (StyleFlags)(int(a) | int(b)); +} + +inline StyleFlags +operator|(const StyleFlags a, const Style b) +{ + return (StyleFlags)(int(a) | int(b)); +} + +/** + Indexing option. +*/ +enum class IndexOption { + SPO = SERD_SPO, /**< Subject, Predicate, Object */ + SOP = SERD_SOP, /**< Subject, Object, Predicate */ + OPS = SERD_OPS, /**< Object, Predicate, Subject */ + OSP = SERD_OSP, /**< Object, Subject, Predicate */ + PSO = SERD_PSO, /**< Predicate, Subject, Object */ + POS = SERD_POS /**< Predicate, Object, Subject */ +}; + +inline IndexOptions +operator|(const IndexOption a, const IndexOption b) +{ + return (IndexOptions)(int(a) | int(b)); +} + +inline IndexOptions +operator|(const IndexOptions a, const IndexOption b) +{ + return (IndexOptions)(int(a) | int(b)); +} + +/** + @name String Utilities + @{ +*/ + +inline const char* +strerror(const Status status) +{ + return serd_strerror((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 +{ +public: + explicit Node(SerdNode* ptr) : Copyable(ptr) {} + explicit Node(const SerdNode* ptr) : Copyable(ptr) {} + + Type type() const { return 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 serd_node_get_flags(c_obj()); } + + Optional datatype() const + { + return Node(serd_node_get_datatype(c_obj())); + } + + Optional language() const + { + return Node(serd_node_get_language(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() : 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())); +} + +// TODO: serd_node_resolve? + +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& root = {}) +{ + return Node(serd_new_relative_uri(str, base.c_obj(), root.ptr())); +} + +inline Node +make_decimal(double d, + unsigned frac_digits, + const Optional& datatype = {}) +{ + return Node(serd_new_decimal(d, frac_digits, datatype.ptr())); +} + +inline Node +make_integer(int64_t i, const Optional& datatype = {}) +{ + return Node(serd_new_integer(i, datatype.ptr())); +} + +inline Node +make_blob(const void* buf, + size_t size, + bool wrap_lines, + const Optional& datatype = {}) +{ + return Node(serd_new_blob(buf, size, wrap_lines, datatype.ptr())); +} + +/** + @} + @name World + @{ +*/ + +class World : public detail::Wrapper +{ +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_error_sink(SerdErrorSink error_sink, void* handle) + { + serd_world_set_error_sink(c_obj(), error_sink, handle); + } + + // operator SerdWorld*() { return c_obj(); } +}; + +/** + @} + @name Statement + @{ +*/ + +class Statement : public detail::Wrapper +{ +public: + Statement(const SerdStatement* statement) : Wrapper(statement) {} + + Node node(Field field) + { + return Node(serd_statement_get_node(c_obj(), 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), detail::Deleter(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(ptr), detail::Deleter(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 nodes(StatementFlags flags, + const Node& subject, + const Node& predicate, + const Node& object, + const Optional& graph = {}) + { + return _is_user ? Status::Success + : Status(serd_sink_write_nodes(_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())); + } + + // operator const SerdSink*() const { return _sink.get(); } + // operator SerdSink*() { return _sink.get(); } + + const SerdSink* c_sink() const { return _sink.get(); } + SerdSink* c_sink() { return _sink.get(); } + +private: + static SerdStatus s_base(void* handle, const SerdNode* uri) + { + return (SerdStatus)((Sink*)handle)->base(Node(uri)); + } + + static SerdStatus + s_prefix(void* handle, const SerdNode* name, const SerdNode* uri) + { + return (SerdStatus)((Sink*)handle)->prefix(Node(name), Node(uri)); + } + + static SerdStatus s_statement(void* handle, + SerdStatementFlags flags, + const SerdStatement* statement) + { + return (SerdStatus)((Sink*)handle)->statement(flags, statement); + } + + static SerdStatus s_end(void* handle, const SerdNode* node) + { + return (SerdStatus)((Sink*)handle)->end(Node(node)); + } + + std::unique_ptr> _sink; + bool _is_user; +}; + +/** + @} + @name Environment + @{ +*/ + +class Env : public detail::Copyable +{ +public: + explicit Env(const Optional& 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 qualify(const Node& uri) const + { + return Node(serd_env_qualify(c_obj(), uri.c_obj())); + } + + /// Expand `node` into an absolute URI if possible + Optional 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) + { + 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); } + + // operator SerdEnv*() { return c_obj(); } +}; + +/** + @} + @name Reader + @{ +*/ + +class Reader : public detail::Wrapper, public Sink +{ +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& 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 +{ +public: + ByteSink(SerdWriteFunc sink, void* stream, size_t block_size) + : Wrapper(serd_byte_sink_new(sink, stream, block_size)) + { + } + + template + ByteSink(std::basic_ostream& stream) + : Wrapper(serd_byte_sink_new(detail::stream_sink, &stream, 1)) + { + } + + // operator SerdByteSink*() { return c_obj(); } +}; + +class Writer : public detail::Wrapper, public Sink +{ +public: + Writer(World& world, + const Syntax syntax, + const StyleFlags style, + Env& env, + ByteSink& sink) + : Wrapper(serd_writer_new(world.c_obj(), + SerdSyntax(syntax), + style, + env.c_obj(), + (SerdWriteFunc)serd_byte_sink_write, + sink.c_obj())) + , Sink(serd_writer_get_sink(c_obj())) + { + } + + Writer(World& world, + const Syntax syntax, + const StyleFlags style, + Env& env, + SerdWriteFunc write_func, + void* stream) + : Wrapper(serd_writer_new(world.c_obj(), + SerdSyntax(syntax), + style, + env.c_obj(), + write_func, + stream)) + , Sink(serd_writer_get_sink(c_obj())) + { + } + + // FIXME: test + template + Writer(World& world, + const Syntax syntax, + const StyleFlags style, + Env& env, + std::basic_ostream& stream) + : Wrapper(serd_writer_new(world.c_obj(), + SerdSyntax(syntax), + style, + 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 +{ +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) + { + return serd_iter_equals(c_obj(), i.c_obj()); + } + bool operator!=(const Iter& i) + { + return !serd_iter_equals(c_obj(), i.c_obj()); + } +}; + +class Range + : public detail::Copyable +{ +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) + { + return serd_range_equals(c_obj(), i.c_obj()); + } + bool operator!=(const Range& i) + { + return !serd_range_equals(c_obj(), i.c_obj()); + } +}; + +class Model + : public detail::Copyable +{ +public: + using value_type = Statement; + using iterator = Iter; + using const_iterator = Iter; + + Model(World& world, SerdIndexOptions indices, SerdModelFlags flags) + : Copyable(serd_model_new(world.c_obj(), indices, flags)) + { + } + + Model(World& world, IndexOption indices, SerdModelFlags flags) + : Copyable(serd_model_new(world.c_obj(), + (SerdIndexOptions)indices, + flags)) + { + } + + 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& g = {}) + { + serd_model_add(c_obj(), s.c_obj(), p.c_obj(), o.c_obj(), g.ptr()); + } + + Iter find(const Optional& s, + const Optional& p, + const Optional& o, + const Optional& g = {}) + { + return Iter( + serd_model_find(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr())); + } + + Range range(const Optional& s, + const Optional& p, + const Optional& o, + const Optional& g = {}) + { + return Range( + serd_model_range(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr())); + } + + Optional get(const Optional& s, + const Optional& p, + const Optional& o, + const Optional& g = {}) + { + return Node( + serd_model_get(c_obj(), s.ptr(), p.ptr(), o.ptr(), g.ptr())); + } + + bool ask(const Optional& s, + const Optional& p, + const Optional& o, + const Optional& g = {}) const + { + return serd_model_ask(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())); } + + // operator SerdModel*() { return c_obj(); } +}; + +class Inserter : public detail::Wrapper, + public Sink +{ +public: + Inserter(Model& model, Env& env, const Optional& 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 */ -- cgit v1.2.1