I just finished my work on a R-client for the BaseX XML-database (see CRAN - Package RBaseX) and am now planning to start working on a similar client in SWI-prolog.
The (all) clients are based on raw non-blocking sockets. All input from the client is converted to a raw stream, the server first responds by returning a zero-terminated raw-stream and then adds either a 0x00 (=succes) or 0x01 (=error). When the input stream is binary, 0x00-bytes and 0xFF- bytes are prepended by an extra 0xFF-byte. They have to be removed from the aoutput stream as returned by the server. When the output-stream represents a character-stream, the raw data has to be converted to character data and care has to be taken of control characters which may be included.
I have two questions:
Is there any interest in a Prolog-client?
As you can see, transforming and processing the stream is the hardest part of the work. Are there any prolog-modules with handy functions/predicates for transforming raw-streams (add or remove bytes, split stream into other streams etc.)
These are low-level conversions between some SWI-Prolog types and bytes, as needed by library(protobufs). They are currently not exported from the protobufs module, but that can be easily changed – you can see how they’re used in protobufs.pl.
If you need these extended a bit, I can probably help you.
Depending on experience, performance impact, etc., I’d consider doing most of the low-level work in C(++). That said, Prolog has all that is needed to read and write bytes. A few things have no portable solution in pure Prolog, such as converting some bytes into a float or converting bytes in unknown native byte ordering into an integer. The stuff in protobufs come handy, either for sharing or getting inspiration.
Otherwise, the more interfaces the better. In the Prolog world SWI-Prolog is doing quite well considering available interfaces. It is still one of the weak points of Prolog though
I can read C and C++ but luckily I have always been able to avoid real programmming in one of those languages . I’ll try but maybe that I will need your help.
Adoption of a new interface is also promoted when a good implementation example is available. Do you have an idea for a good use case?
My advise would be to get the code working correctly with Prolog, then when it works as you seek and if speed or some other checking is needed, then implement portions in C. Also if you are at the C level then consider SIMD. (ref)
Also, for something as simple as dealing with raw bytes / characters, you might be better off (and slightly more performant) to use the C interface. The protobuf.c file that I mentioned could be a reasonable place to start.
Maybe it would be best if you were to say what “raw byte/character conversion” functionality you want?
Actually, the packaged SWI-cpp.h is the original interface by me. I’m a lousy C++ programmer though. Volker wrote something that is closer to how C++ programmers might want to see Prolog. I have little opinion on that But yes, the packaged one is maintained and since recently works cleanly using g++-11 on very high warning levels. It doesn’t do much more than a bit of basic type conversion and exception forwarding though. It also doesn’t cover the entire SWI-Prolog C API, but you can freely mix the two in you C++ code.
It’s been 10+ years since I’ve programmed in C++, so I took a quick look at Google C++ Style Guide to remind myself of the many problem areas in that language. There have been a lot of changes in C++ over the years; for example with copyable and movable types, smart pointers, etc. So, it would be good for someone with recent C++ experience to review the C++ interface(s).
I’m starting to review SWI-cpp.h, by applying the Google style guide. So far, it’s found a number of subtle bugs, mainly to do with implicit casts that weren’t doing what was expected (e.g., treating a size_t as a term_t). I’ve also found some bugs in rocksdb4.pl.cpp that are related, and it wouldn’t surprise me if other code that depends on SWI-cpp.h has similar bugs.
I’m also moving some of the public fields to private or protected – typically they can be accessed by static casts. This might break some code, but hopefully not much, and it has caught a few other subtle bugs.
BTW, I think we should move to C++17 … @jan – where would I make the change?
I’m thinking that the use of operator= for unification of terms is a bad idea. Part of the problem is that there’s a default operator= and this can lead to subtle bugs (I’ve already found a few places where implicit conversions caused a bug)…
Instead, I propose changing the = operator from, e.g. int PlTerm::operator =(const PlTerm &t2);
to bool PlTerm::unify(const PlTerm &t2);
At a conceptual level the only sensible implementation of = between two terms is unification. We notably do not want to copy the term reference itself on =. I’m all in favor of getting rid of ambiguities. We should also be careful that this may be in use in quite a few places and thus break a lot of code, as well as to make sure that the resulting semantics for = is not doing any harm.
On a Linux system it simply provides packages/swipl-win/swipl-win. Same happens on the Mac, but if I recall well the result doesn’t run out of the box. Note that the Qt version is not used on Windows. That uses a rather simple console written as my first exercise in programming on Windows ('95 )
Only in the documentation, I guess. The overall policy of SWI-Prolog is to keep its interfaces running on all supported OSes, libraries and tool chains from the oldest to the latest. The latter is a hard requirement. Old tools may be abandoned if there is a pressing reason. If possible though we try to keep backward compatibility, possibly with loss of some new features.
In the case of rocksdb, I had to explicitly specify CPPFLAGS=-std=c++17, to get it to compile with the current RocksDB code.
I noticed that cmake has set(CMAKE_CXX_STANDARD 17) but I don’t know how to check that it takes effect, nor how to propagate that to packages such as pack(rocksdb). (Presumably this directive should be added to packages/cpp/CMakeLists.txt?)
From what I’ve read of Volker Wysk’s documentation, his C++ interface doesn’t seem suitable for writing foreign code extensions to SWI-Prolog – it seems to be mainly targeted towards calling Prolog from C++.
@jan 's SWI-cpp.h is a fairly thin wrapper on the existing SWI-Prolog.h interface. It feels pretty natural to me as an ex-C++ programmer, especially the use of exceptions (replacing PL_raise_exception() and checking return codes in the C code). In a few places i think it abuses some esoteric features of C++ (overloading the assignment operator) and leaves open the possibility of implicit type conversions that don’t do what the programmer wants. (I’m fairly conservative in that respect, based on bitter experience with other people’s code)
I think I’ll start a new thread, discussing SWI-cpp.h and the changes I’d like to make in it.
You are too kind for me I’m a lousy C++ programmer (and intend to keep it that way). B.t.w. I know a real C++ programmer has been working on the C++ interface. You may want to consider his work. Contact me offline if you are interested and I’ll check with him whether he is willing (and able) to share this work.
My biggest criticism of SWI-cpp.h is that it uses some features of C++ that experienced C++ programmers avoid because they’re error-prone (that’s one of the reasons Google has a C++ style guide – it’s to prevent too much cleverness … especially as the C++ template language is Turing complete). And there are a few C-isms in the code (such as using int instead of bool) and subtleties about how constructor member initialization work.
Hi Peter and Jan,
My progress in my Prolog programming work is slow. This is partly because there are many things - particularly in the area of C++ programming - that I need to understand before I can really come up with results. However, I am still following this thread with interest.
Ben