cannam@110: /* cannam@110: Copyright 2011-2013 David Robillard cannam@110: cannam@110: Permission to use, copy, modify, and/or distribute this software for any cannam@110: purpose with or without fee is hereby granted, provided that the above cannam@110: copyright notice and this permission notice appear in all copies. cannam@110: cannam@110: THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES cannam@110: WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF cannam@110: MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR cannam@110: ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES cannam@110: WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN cannam@110: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF cannam@110: OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. cannam@110: */ cannam@110: cannam@110: /** cannam@110: @file sordmm.hpp cannam@110: Public Sord C++ API. cannam@110: */ cannam@110: cannam@110: #ifndef SORD_SORDMM_HPP cannam@110: #define SORD_SORDMM_HPP cannam@110: cannam@110: #include cannam@110: #include cannam@110: #include cannam@110: #include cannam@110: #include cannam@110: #include cannam@110: #include cannam@110: cannam@110: #include "serd/serd.h" cannam@110: #include "sord/sord.h" cannam@110: cannam@110: #define SORD_NS_XSD "http://www.w3.org/2001/XMLSchema#" cannam@110: cannam@110: namespace Sord { cannam@110: cannam@110: /** Utility base class to prevent copying. */ cannam@110: class Noncopyable { cannam@110: protected: cannam@110: Noncopyable() {} cannam@110: ~Noncopyable() {} cannam@110: private: cannam@110: Noncopyable(const Noncopyable&); cannam@110: const Noncopyable& operator=(const Noncopyable&); cannam@110: }; cannam@110: cannam@110: /** C++ wrapper for a Sord object. */ cannam@110: template cannam@110: class Wrapper { cannam@110: public: cannam@110: inline Wrapper(T c_obj = NULL) : _c_obj(c_obj) {} cannam@110: cannam@110: inline T c_obj() { return _c_obj; } cannam@110: inline const T c_obj() const { return _c_obj; } cannam@110: cannam@110: protected: cannam@110: T _c_obj; cannam@110: }; cannam@110: cannam@110: /** Collection of RDF namespaces with prefixes. */ cannam@110: class Namespaces : public Wrapper { cannam@110: public: cannam@110: Namespaces() : Wrapper(serd_env_new(NULL)) {} cannam@110: ~Namespaces() { serd_env_free(_c_obj); } cannam@110: cannam@110: static inline SerdNode string_to_node(SerdType type, const std::string& s) { cannam@110: SerdNode ret = { cannam@110: (const uint8_t*)s.c_str(), s.length(), s.length(), 0, type }; cannam@110: return ret; cannam@110: } cannam@110: cannam@110: inline void add(const std::string& name, cannam@110: const std::string& uri) { cannam@110: const SerdNode name_node = string_to_node(SERD_LITERAL, name); cannam@110: const SerdNode uri_node = string_to_node(SERD_URI, uri); cannam@110: serd_env_set_prefix(_c_obj, &name_node, &uri_node); cannam@110: } cannam@110: cannam@110: inline std::string qualify(std::string uri) const { cannam@110: const SerdNode uri_node = string_to_node(SERD_URI, uri); cannam@110: SerdNode prefix; cannam@110: SerdChunk suffix; cannam@110: if (serd_env_qualify(_c_obj, &uri_node, &prefix, &suffix)) { cannam@110: std::string ret((const char*)prefix.buf, prefix.n_bytes); cannam@110: ret.append(":").append((const char*)suffix.buf, suffix.len); cannam@110: return ret; cannam@110: } cannam@110: return uri; cannam@110: } cannam@110: cannam@110: inline std::string expand(const std::string& curie) const { cannam@110: assert(curie.find(":") != std::string::npos); cannam@110: SerdNode curie_node = string_to_node(SERD_CURIE, curie); cannam@110: SerdChunk uri_prefix; cannam@110: SerdChunk uri_suffix; cannam@110: if (!serd_env_expand(_c_obj, &curie_node, &uri_prefix, &uri_suffix)) { cannam@110: std::string ret((const char*)uri_prefix.buf, uri_prefix.len); cannam@110: ret.append((const char*)uri_suffix.buf, uri_suffix.len); cannam@110: return ret; cannam@110: } cannam@110: std::cerr << "CURIE `" << curie << "' has unknown prefix." << std::endl; cannam@110: return curie; cannam@110: } cannam@110: }; cannam@110: cannam@110: /** Sord library state. */ cannam@110: class World : public Noncopyable, public Wrapper { cannam@110: public: cannam@110: inline World() cannam@110: : _next_blank_id(0) cannam@110: { cannam@110: _c_obj = sord_world_new(); cannam@110: } cannam@110: cannam@110: inline ~World() { cannam@110: sord_world_free(_c_obj); cannam@110: } cannam@110: cannam@110: inline uint64_t blank_id() { return _next_blank_id++; } cannam@110: cannam@110: inline void add_prefix(const std::string& prefix, const std::string& uri) { cannam@110: _prefixes.add(prefix, uri); cannam@110: } cannam@110: cannam@110: inline const Namespaces& prefixes() const { return _prefixes; } cannam@110: inline SordWorld* world() { return _c_obj; } cannam@110: cannam@110: private: cannam@110: Namespaces _prefixes; cannam@110: std::set _blank_ids; cannam@110: uint64_t _next_blank_id; cannam@110: }; cannam@110: cannam@110: /** An RDF Node (resource, literal, etc) cannam@110: */ cannam@110: class Node : public Wrapper { cannam@110: public: cannam@110: enum Type { cannam@110: UNKNOWN = 0, cannam@110: URI = SORD_URI, cannam@110: BLANK = SORD_BLANK, cannam@110: LITERAL = SORD_LITERAL cannam@110: }; cannam@110: cannam@110: inline Node() : Wrapper(NULL), _world(NULL) {} cannam@110: cannam@110: inline Node(World& world, Type t, const std::string& s); cannam@110: inline Node(World& world); cannam@110: inline Node(World& world, const SordNode* node); cannam@110: inline Node(World& world, SordNode* node, bool copy=false); cannam@110: inline Node(const Node& other); cannam@110: inline ~Node(); cannam@110: cannam@110: inline Type type() const { cannam@110: return _c_obj ? (Type)sord_node_get_type(_c_obj) : UNKNOWN; cannam@110: } cannam@110: cannam@110: inline const SordNode* get_node() const { return _c_obj; } cannam@110: inline SordNode* get_node() { return _c_obj; } cannam@110: cannam@110: const SerdNode* to_serd_node() { cannam@110: return sord_node_to_serd_node(_c_obj); cannam@110: } cannam@110: cannam@110: inline bool is_valid() const { return type() != UNKNOWN; } cannam@110: cannam@110: inline bool operator<(const Node& other) const { cannam@110: if (type() != other.type()) { cannam@110: return type() < other.type(); cannam@110: } else { cannam@110: return to_string() < other.to_string(); cannam@110: } cannam@110: } cannam@110: cannam@110: Node& operator=(const Node& other) { cannam@110: if (&other != this) { cannam@110: if (_c_obj) { cannam@110: sord_node_free(_world->c_obj(), _c_obj); cannam@110: } cannam@110: _world = other._world; cannam@110: _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL; cannam@110: } cannam@110: return *this; cannam@110: } cannam@110: cannam@110: inline bool operator==(const Node& other) const { cannam@110: return sord_node_equals(_c_obj, other._c_obj); cannam@110: } cannam@110: cannam@110: inline const uint8_t* to_u_string() const; cannam@110: inline const char* to_c_string() const; cannam@110: inline std::string to_string() const; cannam@110: cannam@110: inline bool is_literal_type(const char* type_uri) const; cannam@110: cannam@110: inline bool is_uri() const { return _c_obj && type() == URI; } cannam@110: inline bool is_blank() const { return _c_obj && type() == BLANK; } cannam@110: inline bool is_int() const { return is_literal_type(SORD_NS_XSD "integer"); } cannam@110: inline bool is_float() const { return is_literal_type(SORD_NS_XSD "decimal"); } cannam@110: inline bool is_bool() const { return is_literal_type(SORD_NS_XSD "boolean"); } cannam@110: cannam@110: inline int to_int() const; cannam@110: inline float to_float() const; cannam@110: inline bool to_bool() const; cannam@110: cannam@110: inline static Node blank_id(World& world, const std::string base="b") { cannam@110: const uint64_t num = world.blank_id(); cannam@110: std::ostringstream ss; cannam@110: ss << base << num; cannam@110: return Node(world, Node::BLANK, ss.str()); cannam@110: } cannam@110: cannam@110: private: cannam@110: World* _world; cannam@110: }; cannam@110: cannam@110: inline std::ostream& cannam@110: operator<<(std::ostream& os, const Node& node) cannam@110: { cannam@110: return os << node.to_string(); cannam@110: } cannam@110: cannam@110: class URI : public Node { cannam@110: public: cannam@110: inline URI(World& world, const std::string& s) cannam@110: : Node(world, Node::URI, s) {} cannam@110: inline URI(World& world, const std::string& s, const std::string& base) cannam@110: : Node(world, sord_new_relative_uri(world.world(), cannam@110: (const uint8_t*)s.c_str(), cannam@110: (const uint8_t*)base.c_str())) cannam@110: {} cannam@110: }; cannam@110: cannam@110: class Curie : public Node { cannam@110: public: cannam@110: inline Curie(World& world, const std::string& s) cannam@110: : Node(world, Node::URI, world.prefixes().expand(s)) {} cannam@110: }; cannam@110: cannam@110: class Literal : public Node { cannam@110: public: cannam@110: inline Literal(World& world, const std::string& s) cannam@110: : Node(world, Node::LITERAL, s) {} cannam@110: cannam@110: static inline Node decimal(World& world, double d, unsigned frac_digits) { cannam@110: const SerdNode val = serd_node_new_decimal(d, 7); cannam@110: const SerdNode type = serd_node_from_string( cannam@110: SERD_URI, (const uint8_t*)SORD_NS_XSD "decimal"); cannam@110: cannam@110: return Node( cannam@110: world, cannam@110: sord_node_from_serd_node( cannam@110: world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL), cannam@110: false); cannam@110: } cannam@110: cannam@110: static inline Node integer(World& world, int64_t i) { cannam@110: const SerdNode val = serd_node_new_integer(i); cannam@110: const SerdNode type = serd_node_from_string( cannam@110: SERD_URI, (const uint8_t*)SORD_NS_XSD "integer"); cannam@110: cannam@110: return Node( cannam@110: world, cannam@110: sord_node_from_serd_node( cannam@110: world.c_obj(), world.prefixes().c_obj(), &val, &type, NULL), cannam@110: false); cannam@110: } cannam@110: }; cannam@110: cannam@110: inline cannam@110: Node::Node(World& world, Type type, const std::string& s) cannam@110: : _world(&world) cannam@110: { cannam@110: switch (type) { cannam@110: case URI: cannam@110: _c_obj = sord_new_uri( cannam@110: world.world(), (const unsigned char*)s.c_str()); cannam@110: break; cannam@110: case LITERAL: cannam@110: _c_obj = sord_new_literal( cannam@110: world.world(), NULL, (const unsigned char*)s.c_str(), NULL); cannam@110: break; cannam@110: case BLANK: cannam@110: _c_obj = sord_new_blank( cannam@110: world.world(), (const unsigned char*)s.c_str()); cannam@110: break; cannam@110: default: cannam@110: _c_obj = NULL; cannam@110: } cannam@110: cannam@110: assert(this->type() == type); cannam@110: } cannam@110: cannam@110: inline cannam@110: Node::Node(World& world) cannam@110: : _world(&world) cannam@110: { cannam@110: Node me = blank_id(world); cannam@110: *this = me; cannam@110: } cannam@110: cannam@110: inline cannam@110: Node::Node(World& world, const SordNode* node) cannam@110: : _world(&world) cannam@110: { cannam@110: _c_obj = sord_node_copy(node); cannam@110: } cannam@110: cannam@110: inline cannam@110: Node::Node(World& world, SordNode* node, bool copy) cannam@110: : _world(&world) cannam@110: { cannam@110: _c_obj = copy ? sord_node_copy(node) : node; cannam@110: } cannam@110: cannam@110: inline cannam@110: Node::Node(const Node& other) cannam@110: : Wrapper() cannam@110: , _world(other._world) cannam@110: { cannam@110: if (_world) { cannam@110: _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : NULL; cannam@110: } cannam@110: cannam@110: assert((!_c_obj && !other._c_obj) || to_string() == other.to_string()); cannam@110: } cannam@110: cannam@110: inline cannam@110: Node::~Node() cannam@110: { cannam@110: if (_world) { cannam@110: sord_node_free(_world->c_obj(), _c_obj); cannam@110: } cannam@110: } cannam@110: cannam@110: inline std::string cannam@110: Node::to_string() const cannam@110: { cannam@110: return _c_obj ? (const char*)sord_node_get_string(_c_obj) : ""; cannam@110: } cannam@110: cannam@110: inline const char* cannam@110: Node::to_c_string() const cannam@110: { cannam@110: return (const char*)sord_node_get_string(_c_obj); cannam@110: } cannam@110: cannam@110: inline const uint8_t* cannam@110: Node::to_u_string() const cannam@110: { cannam@110: return sord_node_get_string(_c_obj); cannam@110: } cannam@110: cannam@110: inline bool cannam@110: Node::is_literal_type(const char* type_uri) const cannam@110: { cannam@110: if (_c_obj && sord_node_get_type(_c_obj) == SORD_LITERAL) { cannam@110: const SordNode* datatype = sord_node_get_datatype(_c_obj); cannam@110: if (datatype && !strcmp((const char*)sord_node_get_string(datatype), cannam@110: type_uri)) cannam@110: return true; cannam@110: } cannam@110: return false; cannam@110: } cannam@110: cannam@110: inline int cannam@110: Node::to_int() const cannam@110: { cannam@110: assert(is_int()); cannam@110: char* endptr; cannam@110: return strtol((const char*)sord_node_get_string(_c_obj), &endptr, 10); cannam@110: } cannam@110: cannam@110: inline float cannam@110: Node::to_float() const cannam@110: { cannam@110: assert(is_float()); cannam@110: char* endptr; cannam@110: return serd_strtod((const char*)sord_node_get_string(_c_obj), &endptr); cannam@110: } cannam@110: cannam@110: inline bool cannam@110: Node::to_bool() const cannam@110: { cannam@110: assert(is_bool()); cannam@110: return !strcmp((const char*)sord_node_get_string(_c_obj), "true"); cannam@110: } cannam@110: cannam@110: struct Iter : public Wrapper { cannam@110: inline Iter(World& world, SordIter* c_obj) cannam@110: : Wrapper(c_obj), _world(world) {} cannam@110: inline ~Iter() { sord_iter_free(_c_obj); } cannam@110: inline bool end() const { return sord_iter_end(_c_obj); } cannam@110: inline bool next() const { return sord_iter_next(_c_obj); } cannam@110: inline Iter& operator++() { cannam@110: assert(!end()); cannam@110: next(); cannam@110: return *this; cannam@110: } cannam@110: inline const Node get_subject() const { cannam@110: SordQuad quad; cannam@110: sord_iter_get(_c_obj, quad); cannam@110: return Node(_world, quad[SORD_SUBJECT]); cannam@110: } cannam@110: inline const Node get_predicate() const { cannam@110: SordQuad quad; cannam@110: sord_iter_get(_c_obj, quad); cannam@110: return Node(_world, quad[SORD_PREDICATE]); cannam@110: } cannam@110: inline const Node get_object() const { cannam@110: SordQuad quad; cannam@110: sord_iter_get(_c_obj, quad); cannam@110: return Node(_world, quad[SORD_OBJECT]); cannam@110: } cannam@110: World& _world; cannam@110: }; cannam@110: cannam@110: /** An RDF Model (collection of triples). cannam@110: */ cannam@110: class Model : public Noncopyable, public Wrapper { cannam@110: public: cannam@110: inline Model(World& world, cannam@110: const std::string& base_uri, cannam@110: unsigned indices = (SORD_SPO | SORD_OPS), cannam@110: bool graphs = true); cannam@110: cannam@110: inline ~Model(); cannam@110: cannam@110: inline const Node& base_uri() const { return _base; } cannam@110: cannam@110: size_t num_quads() const { return sord_num_quads(_c_obj); } cannam@110: cannam@110: inline void load_file(SerdEnv* env, cannam@110: SerdSyntax syntax, cannam@110: const std::string& uri, cannam@110: const std::string& base_uri=""); cannam@110: cannam@110: inline void load_string(SerdEnv* env, cannam@110: SerdSyntax syntax, cannam@110: const char* str, cannam@110: size_t len, cannam@110: const std::string& base_uri); cannam@110: cannam@110: inline SerdStatus write_to_file( cannam@110: const std::string& uri, cannam@110: SerdSyntax syntax = SERD_TURTLE, cannam@110: SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED cannam@110: |SERD_STYLE_CURIED cannam@110: |SERD_STYLE_RESOLVED)); cannam@110: cannam@110: inline std::string write_to_string( cannam@110: const std::string& base_uri, cannam@110: SerdSyntax syntax = SERD_TURTLE, cannam@110: SerdStyle style = (SerdStyle)(SERD_STYLE_ABBREVIATED cannam@110: |SERD_STYLE_CURIED cannam@110: |SERD_STYLE_RESOLVED)); cannam@110: cannam@110: inline void add_statement(const Node& subject, cannam@110: const Node& predicate, cannam@110: const Node& object); cannam@110: cannam@110: inline Iter find(const Node& subject, cannam@110: const Node& predicate, cannam@110: const Node& object); cannam@110: cannam@110: inline Node get(const Node& subject, cannam@110: const Node& predicate, cannam@110: const Node& object); cannam@110: cannam@110: inline World& world() const { return _world; } cannam@110: cannam@110: private: cannam@110: World& _world; cannam@110: Node _base; cannam@110: SerdWriter* _writer; cannam@110: size_t _next_blank_id; cannam@110: }; cannam@110: cannam@110: /** Create an empty in-memory RDF model. cannam@110: */ cannam@110: inline cannam@110: Model::Model(World& world, cannam@110: const std::string& base_uri, cannam@110: unsigned indices, cannam@110: bool graphs) cannam@110: : _world(world) cannam@110: , _base(world, Node::URI, base_uri) cannam@110: , _writer(NULL) cannam@110: { cannam@110: _c_obj = sord_new(_world.world(), indices, graphs); cannam@110: } cannam@110: cannam@110: inline void cannam@110: Model::load_string(SerdEnv* env, cannam@110: SerdSyntax syntax, cannam@110: const char* str, cannam@110: size_t len, cannam@110: const std::string& base_uri) cannam@110: { cannam@110: SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL); cannam@110: serd_reader_read_string(reader, (const uint8_t*)str); cannam@110: serd_reader_free(reader); cannam@110: } cannam@110: cannam@110: inline Model::~Model() cannam@110: { cannam@110: sord_free(_c_obj); cannam@110: } cannam@110: cannam@110: inline void cannam@110: Model::load_file(SerdEnv* env, cannam@110: SerdSyntax syntax, cannam@110: const std::string& data_uri, cannam@110: const std::string& base_uri) cannam@110: { cannam@110: uint8_t* path = serd_file_uri_parse((const uint8_t*)data_uri.c_str(), NULL); cannam@110: if (!path) { cannam@110: fprintf(stderr, "Failed to parse file URI <%s>\n", data_uri.c_str()); cannam@110: return; cannam@110: } cannam@110: cannam@110: // FIXME: blank prefix parameter? cannam@110: SerdReader* reader = sord_new_reader(_c_obj, env, syntax, NULL); cannam@110: serd_reader_read_file(reader, path); cannam@110: serd_reader_free(reader); cannam@110: free(path); cannam@110: } cannam@110: cannam@110: inline SerdStatus cannam@110: Model::write_to_file(const std::string& uri, SerdSyntax syntax, SerdStyle style) cannam@110: { cannam@110: uint8_t* path = serd_file_uri_parse((const uint8_t*)uri.c_str(), NULL); cannam@110: if (!path) { cannam@110: fprintf(stderr, "Failed to parse file URI <%s>\n", uri.c_str()); cannam@110: return SERD_ERR_BAD_ARG; cannam@110: } cannam@110: cannam@110: FILE* const fd = fopen((const char*)path, "w"); cannam@110: if (!fd) { cannam@110: fprintf(stderr, "Failed to open file %s\n", path); cannam@110: free(path); cannam@110: return SERD_ERR_UNKNOWN; cannam@110: } cannam@110: free(path); cannam@110: cannam@110: SerdURI base_uri = SERD_URI_NULL; cannam@110: if (serd_uri_parse((const uint8_t*)uri.c_str(), &base_uri)) { cannam@110: fprintf(stderr, "Invalid base URI <%s>\n", uri.c_str()); cannam@110: fclose(fd); cannam@110: return SERD_ERR_BAD_ARG; cannam@110: } cannam@110: cannam@110: SerdWriter* writer = serd_writer_new(syntax, cannam@110: style, cannam@110: _world.prefixes().c_obj(), cannam@110: &base_uri, cannam@110: serd_file_sink, cannam@110: fd); cannam@110: cannam@110: serd_env_foreach(_world.prefixes().c_obj(), cannam@110: (SerdPrefixSink)serd_writer_set_prefix, cannam@110: writer); cannam@110: cannam@110: sord_write(_c_obj, writer, 0); cannam@110: serd_writer_free(writer); cannam@110: fclose(fd); cannam@110: cannam@110: return SERD_SUCCESS; cannam@110: } cannam@110: cannam@110: static size_t cannam@110: string_sink(const void* buf, size_t len, void* stream) cannam@110: { cannam@110: std::string* str = (std::string*)stream; cannam@110: str->append((const char*)buf, len); cannam@110: return len; cannam@110: } cannam@110: cannam@110: inline std::string cannam@110: Model::write_to_string(const std::string& base_uri_str, cannam@110: SerdSyntax syntax, cannam@110: SerdStyle style) cannam@110: { cannam@110: SerdURI base_uri = SERD_URI_NULL; cannam@110: if (serd_uri_parse((const uint8_t*)base_uri_str.c_str(), &base_uri)) { cannam@110: fprintf(stderr, "Invalid base URI <%s>\n", base_uri_str.c_str()); cannam@110: return ""; cannam@110: } cannam@110: cannam@110: std::string ret; cannam@110: cannam@110: SerdWriter* writer = serd_writer_new(syntax, cannam@110: style, cannam@110: _world.prefixes().c_obj(), cannam@110: &base_uri, cannam@110: string_sink, cannam@110: &ret); cannam@110: cannam@110: serd_env_foreach(_world.prefixes().c_obj(), cannam@110: (SerdPrefixSink)serd_writer_set_prefix, cannam@110: writer); cannam@110: cannam@110: sord_write(_c_obj, writer, 0); cannam@110: cannam@110: serd_writer_free(writer); cannam@110: return ret; cannam@110: } cannam@110: cannam@110: inline void cannam@110: Model::add_statement(const Node& subject, cannam@110: const Node& predicate, cannam@110: const Node& object) cannam@110: { cannam@110: SordQuad quad = { subject.c_obj(), cannam@110: predicate.c_obj(), cannam@110: object.c_obj(), cannam@110: NULL }; cannam@110: cannam@110: sord_add(_c_obj, quad); cannam@110: } cannam@110: cannam@110: inline Iter cannam@110: Model::find(const Node& subject, cannam@110: const Node& predicate, cannam@110: const Node& object) cannam@110: { cannam@110: SordQuad quad = { subject.c_obj(), cannam@110: predicate.c_obj(), cannam@110: object.c_obj(), cannam@110: NULL }; cannam@110: cannam@110: return Iter(_world, sord_find(_c_obj, quad)); cannam@110: } cannam@110: cannam@110: inline Node cannam@110: Model::get(const Node& subject, cannam@110: const Node& predicate, cannam@110: const Node& object) cannam@110: { cannam@110: SordNode* c_node = sord_get( cannam@110: _c_obj, subject.c_obj(), predicate.c_obj(), object.c_obj(), NULL); cannam@110: Node node(_world, c_node); cannam@110: sord_node_free(_world.c_obj(), c_node); cannam@110: return node; cannam@110: } cannam@110: cannam@110: } // namespace Sord cannam@110: cannam@110: #endif // SORD_SORDMM_HPP cannam@110: