One of the things I like about C++ is overloading function parameters because it can allow for programs that are organized in more of a pattern-matching style. I’m trying to find a safe and efficient way to expose overloaded C++ functions as Prolog predicates with multiple clauses using the SWI-Prolog C++ FFI.
Here is an example of my PlBlob
subclasses and a overloaded function/multi-clause predicate mapping (followed by specific questions):
struct blob_sfVertex;
static PL_blob_t blob_sf_vertex = PL_BLOB_DEFINITION (blob_sfVertex, "blob_sf_vertex");
struct blob_sfVertex : public PlBlob
{
std::unique_ptr<sf::Vertex> foreign_object;
PL_BLOB_SIZE
//Default constructor
explicit blob_sfVertex ()
: PlBlob (&blob_sf_vertex),
foreign_object (std::make_unique<sf::Vertex> ())
{}
// Copy constructor
explicit blob_sfVertex (sf::Vertex& x)
: PlBlob (&blob_sf_vertex),
foreign_object (std::make_unique<sf::Vertex> (x))
{}
explicit blob_sfVertex (const sf::Vector2f& thePosition,const sf::Vector2f& theTexCoords)
: PlBlob (&blob_sf_vertex),
foreign_object (std::make_unique<sf::Vertex> (thePosition,theTexCoords))
{}
// More constructors...
};
I have my predicates defined like this
/*
%! sf_vertex_vertex(+Position:blob_sfVector2f, +TextureCoordinates:blob_sfVector2f, -Vertex:blob_sfVertex).
%
% Construct the vertex from its position and texture coordinates.
*/
PREDICATE (sf_vertex_vertex, 3)
{
auto thePosition = PlBlobV<blob_sfVector2f>::cast_ex (A1, blob_sf_vector2f);
auto theTexCoords = PlBlobV<blob_sfVector2f>::cast_ex (A2, blob_sf_vector2f);
auto foreign_object = std::unique_ptr<PlBlob>
(new blob_sfVertex (*thePosition->foreign_object,*theTexCoords->foreign_object));
return A3.unify_blob (&foreign_object);
}
/*
%! sf_vertex_vertex(+Position:blob_sfVector2f, +Color:blob_sfVector2f, -Vertex:blob_sfVertex).
%
% Construct the vertex from its position and color.
*/
PREDICATE (sf_vertex_vertex, 3)
{
auto thePosition = PlBlobV<blob_sfVector2f>::cast_ex (A1, blob_sf_vector2f);
auto theColor = PlBlobV<blob_sfColor>::cast_ex (A2, blob_sf_color);
auto foreign_object = std::unique_ptr<PlBlob>
(new blob_sfVertex (*thePosition->foreign_object,*theColor->foreign_object));
return A3.unify_blob (&foreign_object);
}
As written, this code will not compile because the PREDICATE
macro will evaluate to a redefinition of sf_vertex_vertex
.
I can think of 3 ways of making this work:
-
Renaming the predicates so they are different names for each specialization. This is what I’ve done, temporarily, so I can compile the code and test the predicates. It might not be the worst thing in the world, but I don’t want keep it this way because I want the predicates to match the source interface (sf::Vertex Class Reference (SFML / Learn / 2.6.1 Documentation)).
-
I thought I could use
PL_is_blob
and maybe some kind of RTTI to find out the type of aPlTerm
argument, and then use some kind of switch statement to handle the right type. -
Use
PREDICATE_NONDET
and handle the blob type casting exceptions to enter redo. I’d probably just keep the specially definitions described in 1. and iterate over them. I could also do this in Prolog and it would probably be equivalent?
Option 3. seems like the most natural because that’s what I’ve done when I’ve written predicates with differently typed parameters. Does that seem right?