Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 93 additions & 53 deletions SWI-cpp2.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,32 @@ particularly integer conversions.
class PlAtom;
class PlTerm;
class PlTermv;
void PlCheck(int rc);


// A pseudo-exception for quick exist on failure, for use by the unify
// methods. This is special-cased in the PREDICATE et al macros.
// Note that it is *not* a subclass of PlException. See the
// documentation for more details on how this works with returning
// Prolog failure and returning exceptions.
class PlFail
{
public:
explicit PlFail() {}
};


// Throw PlFail on failure or exception. This exception is caught by
// the PREDICATE, which simply returns false ... if the failure was
// caused by an exception, SWI-Prolog will detect that and turn the
// failure into a Prolog exception. Therefore, there is no need for
// calling PL_exception(0) and doing something different if there is
// a pending Prolog exception (to call PL_exception(0), use
// PlException_qid()).
inline void
PlCheck(int rc)
{ if ( !rc )
throw PlFail();
}


/*******************************
Expand All @@ -105,9 +130,8 @@ template <typename C_t> class WrappedC
bool not_null() const { return C_ != null; }
void verify() const; // Throw exception if is_null()

public:
WrappedC<C_t>() : C_(null) { }
explicit WrappedC<C_t>(C_t v) : C_(v) { }
explicit WrappedC<C_t>(C_t v)
: C_(v) { }
WrappedC<C_t>(const WrappedC<C_t>&) = default;
// WrappedC<C_t>& operator =(const WrappedC<C_t>&) = default; // deprecated/deleted in PlTerm
operator bool() const = delete; // Use not_null() instead
Expand Down Expand Up @@ -159,16 +183,19 @@ class PlStringBuffers
class PlFunctor : public WrappedC<functor_t>
{
public:
PlFunctor(functor_t v = null) : WrappedC<functor_t>() { }
PlFunctor(functor_t v)
: WrappedC<functor_t>(v) { }
// PlFunctor(const char*) is handled by std::string constructor
explicit PlFunctor(const std::string& name, size_t arity);
explicit PlFunctor(const std::wstring& name, size_t arity);

bool operator ==(functor_t to) = delete;

// TODO: use PlPredicate, PlModule when implemented:
predicate_t pred(module_t m) const {
return PL_pred(C_, m);
[[deprecated("use PlPredicate")]] predicate_t pred(module_t m) const {
predicate_t p = PL_pred(C_, m);
if ( p == nullptr )
throw PlFail();
return p;
}

PlAtom name() const;
Expand All @@ -182,8 +209,8 @@ class PlFunctor : public WrappedC<functor_t>
class PlAtom : public WrappedC<atom_t>
{
public:
PlAtom() : WrappedC<atom_t>() { } // Make constructor public
explicit PlAtom(atom_t v) : WrappedC<atom_t>(v) { }
explicit PlAtom(atom_t v)
: WrappedC<atom_t>(v) { }
explicit PlAtom(const std::string& text) // TODO: add encoding
: WrappedC<atom_t>(PL_new_atom_nchars(text.size(), text.data()))
{ verify();
Expand Down Expand Up @@ -269,30 +296,6 @@ class PlAtom : public WrappedC<atom_t>
* GENERIC PROLOG TERM *
*******************************/


// A pseudo-exception for quick exist on failure, for use by the unify
// methods. This is special-cased in the PREDICATE et al macros.
// Note that it is *not* a subclass of PlException.
class PlFail
{
public:
explicit PlFail() {}
};


// Throw PlFail on failure or exception. This exception is caught by
// the PREDICATE, which simply returns false ... if the failure was
// caused by an exception, SWI-Prolog will detect that and turn the
// failure into a Prolog exception. Therefore, there is no need for
// calling PL_exception(0) and doing something different if there is
// a pending Prolog exception (to call PL_exception(0), use
// PlException_qid()).
inline void
PlCheck(int rc)
{ if ( !rc )
throw PlFail();
}

class PlTerm : public WrappedC<term_t>
{
protected:
Expand All @@ -310,7 +313,8 @@ class PlTerm : public WrappedC<term_t>

public:
PlTerm(const PlTerm&) = default;
explicit PlTerm(const PlAtom& a) : WrappedC<term_t>(PL_new_term_ref())
explicit PlTerm(const PlAtom& a)
: WrappedC<term_t>(PL_new_term_ref())
{ verify();
PlCheck(PL_put_atom(C_, a.C_));
}
Expand Down Expand Up @@ -529,13 +533,14 @@ class PlTerm_atom : public PlTerm
class PlTerm_var : public PlTerm
{
public:
explicit PlTerm_var() : PlTerm() {}
explicit PlTerm_var() { } // PlTerm() calls Pl_new_term_ref()
};

class PlTerm_term_t : public PlTerm
{
public:
explicit PlTerm_term_t(term_t t = null) : PlTerm(t) {}
explicit PlTerm_term_t(term_t t)
: PlTerm(t) {}
};

class PlTerm_integer : public PlTerm
Expand Down Expand Up @@ -575,6 +580,36 @@ class PlTerm_recorded : public PlTerm
explicit PlTerm_recorded(record_t r) { PlCheck(PL_recorded(r, C_)); }
};

class PlModule : public WrappedC<module_t>
{
public:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we may have a null constructor. Practically all API functions accept a NULL module as a shorthand for the current calling context, defaulting to user if such a context does not exist (when we make calls to Prolog without being called by Prolog).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a constructor that takes module_t, so PlModule(PlModule::null) is now possible.

explicit PlModule(module_t m)
: WrappedC<module_t>(m) { }
explicit PlModule(const std::string& name)
: WrappedC<module_t>(PL_new_module(PlAtom(name).C_))
{ verify();
}
explicit PlModule(PlAtom name)
: WrappedC<module_t>(PL_new_module(name.C_))
{ verify();
}
};

class PlPredicate : public WrappedC<predicate_t>
{
public:
explicit PlPredicate(predicate_t p)
: WrappedC<predicate_t>(p) { }
explicit PlPredicate(PlFunctor f)
: WrappedC<predicate_t>(PL_pred(f.C_, static_cast<module_t>(PlModule::null)))
{ verify();
}
explicit PlPredicate(PlFunctor f, PlModule m)
: WrappedC<predicate_t>(PL_pred(f.C_, m.C_))
{ verify();
}
};


/*******************************
* TERM VECTOR *
Expand All @@ -587,21 +622,22 @@ class PlTermv
term_t a0_; // A vector of term_t

public:
explicit PlTermv(size_t n)
explicit PlTermv(size_t n = 0)
: size_(n),
a0_(PL_new_term_refs(static_cast<int>(n)))
{ if ( ! a0_ )
a0_(n ? PL_new_term_refs(static_cast<int>(n)) : PlTerm::null)
{ if ( size_ && a0_ == PlTerm::null )
throw PlFail();
}
explicit PlTermv(size_t n, const PlTerm& t0)
: size_(n),
a0_(t0.C_)
{ if ( ! a0_ )
{ if ( size_ && a0_ == PlTerm::null )
throw PlFail();
}

term_t termv() const
{ return a0_;
{ // Note that a0_ can be PlTerm::null if size_ == 0
return a0_;
}

size_t size() const
Expand Down Expand Up @@ -830,6 +866,7 @@ WrappedC<C_t>::verify() const

inline
PlFunctor::PlFunctor(const std::string& name, size_t arity)
: WrappedC<functor_t>(null)
{ PlAtom a(name);
C_ = PL_new_functor(a.C_, arity);
PL_unregister_atom(a.C_);
Expand All @@ -838,6 +875,7 @@ PlFunctor::PlFunctor(const std::string& name, size_t arity)

inline
PlFunctor::PlFunctor(const std::wstring& name, size_t arity)
: WrappedC<functor_t>(null)
{ PlAtom a(name);
C_ = PL_new_functor(a.C_, arity);
PL_unregister_atom(a.C_);
Expand Down Expand Up @@ -1026,7 +1064,7 @@ class PlFrame

private:
void verify()
{ if ( !fid_ )
{ if ( fid_ == static_cast<fid_t>(0) )
throw PlFail();
}
};
Expand All @@ -1045,22 +1083,24 @@ class PlQuery
qid_t qid_;

public:
PlQuery(predicate_t pred, const PlTermv& av, int flags = PL_Q_PASS_EXCEPTION)
: qid_(PL_open_query(static_cast<module_t>(0), flags, pred, av.termv()))
PlQuery(PlPredicate pred, const PlTermv& av, int flags = PL_Q_PASS_EXCEPTION)
: qid_(PL_open_query(static_cast<module_t>(0), flags, pred.C_, av.termv()))
{ verify();
}
// TODO: PlQuery(const wstring& ...)
// TODO: PlQuery({PlAtom,PlFunctor,PlPredicate} ...)
PlQuery(const std::string& name, const PlTermv& av, int flags = PL_Q_PASS_EXCEPTION)
: qid_(PL_open_query(static_cast<module_t>(0), flags,
// TODO: throw if PL_predicate() returns 0
PL_predicate(name.c_str(), static_cast<int>(av.size()), "user"),
: qid_(PL_open_query(static_cast<module_t>(0),
flags,
PlPredicate(PlFunctor(name, av.size())).C_,
av.termv()))
{ verify();
}
PlQuery(const std::string& module, const std::string& name, const PlTermv& av, int flags = PL_Q_PASS_EXCEPTION)
: qid_(PL_open_query(static_cast<module_t>(0), flags,
// TODO: throw if PL_predicate() returns 0
PL_predicate(name.c_str(), static_cast<int>(av.size()), module.c_str()),
: qid_(PL_open_query(static_cast<module_t>(0),
flags,
PlPredicate(PlFunctor(name, av.size()),
PlModule(name)).C_,
av.termv()))
{ verify();
}
Expand Down Expand Up @@ -1098,7 +1138,7 @@ class PlQuery

private:
void verify()
{ if ( !qid_ )
{ if ( qid_ == static_cast<qid_t>(0) )
throw PlFail();
}
};
Expand Down Expand Up @@ -1314,7 +1354,7 @@ PlCompound::PlCompound(const wchar_t *text)
inline
PlCompound::PlCompound(const std::string& text, PlEncoding enc)
{ term_t t = PL_new_term_ref();
if ( ! t )
if ( t == PlTerm::null )
throw PlFail();

// TODO: PL_put_term_from_chars() should take an unsigned int flags
Expand Down
6 changes: 6 additions & 0 deletions pl2cpp2.doc
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ Data Types}).
\classitem{PlFunctor}
A wrapper for \ctype{functor_t}, which maps to the internal
representation of a name/arity pair.
\classitem{PlPredicate}
A wrapper for \ctype{predicate_t}, which maps to the internal
representation of a Prolog predicate.
\classitem{PlModule}
A wrapper for \ctype{module_t}, which maps to the internal
representation of a Prolog module.
\classitem{PlQuery}
Represents opening and enumerating the solutions to a Prolog query.
\classitem{PlFail}
Expand Down
13 changes: 10 additions & 3 deletions test_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ PREDICATE(hello_query, 2)
return true;
}

PREDICATE(call_cut, 1)
{ PlQuery q(A1.as_string(), PlTermv());
PlCheck(q.next_solution());
q.cut();
return true;
}

PREDICATE(hello_call, 1)
{ PlCheck(PlCall(A1));
return true;
Expand Down Expand Up @@ -500,7 +507,7 @@ PREDICATE(ensure_PlTerm_forward_declarations_are_implemented, 0)
PlTerm_atom p_atom4(std::string("abc"));
PlTerm_atom p_atom5(std::wstring(L"世界"));
PlTerm_term_t t_t(PL_new_term_ref());
PlTerm_term_t t_null; // null
PlTerm_term_t t_null(PlTerm::null);
PlTerm_integer t_int1(std::numeric_limits<int>::max());
PlTerm_integer t_int1b(std::numeric_limits<int>::min());
PlTerm_integer t_int2(std::numeric_limits<long>::max());
Expand All @@ -524,7 +531,7 @@ PREDICATE(ensure_PlTerm_forward_declarations_are_implemented, 0)
PlAtom atom3(std::string("atom3"));
PlAtom atom4(std::wstring(L"原子4"));
PlAtom a5a(t_atom1.as_atom());
PlAtom atom_null;
PlAtom atom_null(PlAtom::null);
// The following are unsafe (the as_string() is deleted in the statement):
// const char * x01 = t_var.as_string().c_str();
// const wchar_t *x01a = t_var.as_wstring().c_str();
Expand Down Expand Up @@ -990,7 +997,7 @@ PREDICATE(cpp_options, 3)
int quoted = false;
size_t length = 10;
PlTerm_var callback;
PlAtom token;
PlAtom token(PlAtom::null);
const char *descr = "";
bool opt_all_v = opt_all.as_bool();
int flags = opt_all_v ? OPT_ALL : 0;
Expand Down
15 changes: 13 additions & 2 deletions test_cpp.pl
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@
:- if(\+current_prolog_flag(windows, true)).
% The C++ atom_to_string/2 takes the raw bytes from atom and creates
% a byte string. This doesn't work in Windows as we use a different
% representation. This is dubious anyway. Delete entirely fix when
% the C++ interface properly supports encodings.
% representation. This is dubious anyway.
% TODO: Delete entire fix when the C++ interface properly supports encodings.
% See: https://swi-prolog.discourse.group/t/ann-swi-prolog-9-1-0/5964/3
test(as_string, S == "ä¸\u0096ç\u0095\u008Cå\u009B\u009B") :-
atom_to_string(世界四, S).
test(as_string, S = "hello(ä¸\u0096ç\u0095\u008Cå\u009B\u009B)") :-
Expand Down Expand Up @@ -118,6 +119,16 @@
test(hello_0, Out == "hello world\n") :-
with_output_to(string(Out), hello).

call_cut_test :-
setup_call_cleanup(true,
between(1, 5, _X),
atom_codes(_,_)).

test(call_cut, error(existence_error(procedure,call_cut_test/0))) :-
% This tests that an error in ~PlQuery() is handled properly
% See discussion: https://github.com/SWI-Prolog/packages-cpp/pull/27
call_cut("call_cut_test").

test(term_1, Term = hello(world)) :-
term(Term).

Expand Down