Jump to page: 1 2
Thread overview
#import mapi.h
Mar 21, 2018
ketmar
Mar 21, 2018
Timoses
Mar 21, 2018
nkm1
Mar 22, 2018
Kagamin
Mar 22, 2018
Paul Backus
Mar 23, 2018
Mike Parker
March 21, 2018
Is there an step by step introduction how to convert a C header of an external lib into the right extern(C){} block?

A blog post or tutorial, or chapter in a D book? (I have those from Packt Publishing)

(Especially I am trying to get this used with D:
Montetdb C-API https://www.monetdb.org/Documentation/Manuals/SQLreference/Programming/MAPI
With: https://github.com/snaga/monetdb/blob/master/clients/mapilib/mapi.h)

The page: https://dlang.org/spec/interfaceToC.html is known, but not detailed enough for me.
March 21, 2018
Martin Tschierschke wrote:

> or tutorial

ok, tutorial:

1. learn C.
2. learn D.
3. DO IT!

;-)
March 21, 2018
On Wednesday, 21 March 2018 at 16:22:45 UTC, Martin Tschierschke wrote:
> Is there an step by step introduction how to convert a C header of an external lib into the right extern(C){} block?
>
> A blog post or tutorial, or chapter in a D book? (I have those from Packt Publishing)

While googling I found this:
https://dlang.org/blog/2017/12/05/interfacing-d-with-c-getting-started/

Haven't read it yet, but might give you some more insight.

For automatic conversion I stumbled across Dstep which I so far like a lot:
https://github.com/jacob-carlborg/dstep
March 21, 2018
On Wednesday, 21 March 2018 at 16:22:45 UTC, Martin Tschierschke wrote:
> Is there an step by step introduction how to convert a C header of an external lib into the right extern(C){} block?
>
> A blog post or tutorial, or chapter in a D book? (I have those from Packt Publishing)
>
> (Especially I am trying to get this used with D:
> Montetdb C-API https://www.monetdb.org/Documentation/Manuals/SQLreference/Programming/MAPI
> With: https://github.com/snaga/monetdb/blob/master/clients/mapilib/mapi.h)
>
> The page: https://dlang.org/spec/interfaceToC.html is known, but not detailed enough for me.

The easiest thing to do is to write a wrapper in C. The wrapper will include all necessary headers and provide some easy to use functions that you can call from D. Of course, you'll need to use a C compiler to compile it.
Anyway, this header looks very straightforwar, no particular tricks with the preprocessor. It should be something like this (untested, obviously):

import core.stdc.stdio : FILE;

enum
{
    MAPI_AUTO      = 0,  /* automatic type detection */
    MAPI_TINY      = 1,
    MAPI_UTINY     = 2,
    MAPI_SHORT     = 3,
    MAPI_USHORT    = 4,
    MAPI_INT       = 5,
    MAPI_UINT      = 6,
    MAPI_LONG      = 7,
    MAPI_ULONG     = 8,
    MAPI_LONGLONG  = 9,
    MAPI_ULONGLONG = 10,
    MAPI_CHAR      = 11,
    MAPI_VARCHAR   = 12,
    MAPI_FLOAT     = 13,
    MAPI_DOUBLE    = 14,
    MAPI_DATE      = 15,
    MAPI_TIME      = 16,
    MAPI_DATETIME  = 17,
    MAPI_NUMERIC   = 18,
}

enum int PLACEHOLDER = '?';

enum
{
    MAPI_SEEK_SET = 0,
    MAPI_SEEK_CUR = 1,
    MAPI_SEEK_END = 2,
}

enum
{
    MAPI_TRACE      = 1,
    MAPI_TRACE_LANG = 2,
}

alias MapiMsg = int;

enum
{
    MOK         =  0,
    MERROR      = -1,
    MTIMEOUT    = -2,
    MMORE       = -3,
    MSERVER     = -4,
}

enum
{
    LANG_MAL    = 0,
    LANG_SQL    = 2,
    LANG_JAQL   = 3,
}

/* prompts for MAPI protocol */
enum int PROMPTBEG = '\001'; /* start prompt bracket */

 /* prompt: ready for new query */
const(char)* PROMPT1 = "\001\001\n".ptr;

/* prompt: more data needed */
const(char)* PROMTP2 = "\001\002\n".ptr;

/*
 * The table field information is extracted from the table headers
 * obtained from the server. This list may be extended in the future.
 * The type of both the 'param' and 'binding'
 * variables refer to their underlying C-type. They are used for
 * automatic type coercion between back-end and application.
 */
struct MapiStruct;
alias Mapi = MapiStruct*;

/* this definition is a straight copy from sql/include/sql_query.h */
enum
{
    Q_PARSE     = 0,
    Q_TABLE     = 1,
    Q_UPDATE    = 2,
    Q_SCHEMA    = 3,
    Q_TRANS     = 4,
    Q_PREPARE   = 5,
    Q_BLOCK     = 6,
}

struct MapiStatement;
alias MapiHdl = MapiStatement*;

alias mapi_uint64 = ulong;
alias mapi_int64 = long;

/* three structures used for communicating date/time information */
/* these structs are deliberately compatible with the ODBC versions
   SQL_DATE_STRUCT, SQL_TIME_STRUCT, and SQL_TIMESTAMP_STRUCT */

/* used by MAPI_DATE */
struct MapiDate
{
    short year;
    ushort month;
    ushort day;
}

/* used by MAPI_TIME */
struct MapiTime
{
    ushort hour;
    ushort minute;
    ushort second;
}

/* used by MAPI_DATETIME */
struct MapiDateTime
{
    short year;
    ushort month;
    ushort day;
    ushort hour;
    ushort minute;
    ushort second;
    uint fraction;  /* in 1000 millionths of a second (10e-9) */
}

/* connection-oriented functions */
extern (C)
{
    Mapi mapi_mapi(const char *host,
                   int port,
                   const char *username,
                   const char *password,
                   const char *lang,
                   const char *dbname);


    // and so on...
}
March 22, 2018
On Wednesday, 21 March 2018 at 18:42:43 UTC, nkm1 wrote:
> On Wednesday, 21 March 2018 at 16:22:45 UTC, Martin Tschierschke wrote:
>> [...]
>
> The easiest thing to do is to write a wrapper in C. The wrapper will include all necessary headers and provide some easy to use functions that you can call from D. Of course, you'll need to use a C compiler to compile it.
> Anyway, this header looks very straightforwar, no particular tricks with the preprocessor. It should be something like this (untested, obviously):
>
> [...]
Thank you!
Especially this was a missing link:
>struct MapiStruct;
>alias Mapi = MapiStruct*;
March 22, 2018
On Wednesday, 21 March 2018 at 17:12:07 UTC, Timoses wrote:
> On Wednesday, 21 March 2018 at 16:22:45 UTC, Martin Tschierschke wrote:
>> Is there an step by step introduction how to convert a C header of an external lib into the right extern(C){} block?
>>
>> A blog post or tutorial, or chapter in a D book? (I have those from Packt Publishing)
>
> While googling I found this:
> https://dlang.org/blog/2017/12/05/interfacing-d-with-c-getting-started/
>
> Haven't read it yet, but might give you some more insight.
>
> For automatic conversion I stumbled across Dstep which I so far like a lot:
> https://github.com/jacob-carlborg/dstep

Thank you! I will have a look at both, an Mike Parker is pointing to his book, too,
so now together with the post  from "nkm1" I should be able to make it!

@ketmar, I learned C but the counter of years from that time was stored in a nibble and so I got an overflow...error :-)
March 22, 2018
On Thursday, 22 March 2018 at 12:53:23 UTC, Martin Tschierschke wrote:
> On Wednesday, 21 March 2018 at 17:12:07 UTC, Timoses wrote:
[...]

I got it, my first mini test with C bindings!
Important missing at first: (stderr)

import core.stdc.stdio : stderr, FILE;
and use of toStringz inside:  mapi_query(dbh, toStringz(q) )

After installing monetdb and starting the server with:
https://www.monetdb.org/Documentation/UserGuide/Tutorial

My minimal version of the example compiled with
dmd app.d -I /usr/local/lib/libmapi.so

(My dub.json is still incomplete so the linking failed.)

Here is the code: (only the minimum of mapi.h is icluded!)

```
 std.stdio;
import std.string;

/* interfacing winth monetdb #include mapi.h */
/* minimum to run code of example at: https://www.monetdb.org/Documentation/Manuals/SQLreference/Programming/MAPI */
import core.stdc.stdio : stderr,FILE;
import core.stdc.stdlib;

struct MapiStruct;
alias Mapi = MapiStruct*;

struct MapiStatement;
alias MapiHdl = MapiStatement*;

enum MOK = 0 ;

alias MapiMsg = int;

extern(System){
Mapi mapi_connect(const char* host, int port, const char* username, const char* password, const char* lang, const char* dbname);
MapiHdl mapi_query(Mapi mid, const char *cmd);
int mapi_fetch_row(MapiHdl hdl);
char *mapi_fetch_field(MapiHdl hdl, int fnr);
MapiMsg mapi_error(Mapi mid);
MapiMsg mapi_explain(Mapi mid, FILE* fd);
MapiMsg mapi_destroy(Mapi mid);
MapiMsg mapi_close_handle(MapiHdl hdl);
MapiMsg mapi_explain_result(MapiHdl hdl, FILE* fd);
MapiMsg mapi_next_result(MapiHdl hdl);
MapiMsg mapi_explain_query(MapiHdl hdl, FILE* fd);
char *mapi_result_error(MapiHdl hdl);
}


void die(Mapi dbh, MapiHdl hdl) {
  if (hdl != null) {
    mapi_explain_query(hdl, stderr);
    do {
      if (mapi_result_error(hdl) != null)
        mapi_explain_result(hdl, stderr);
    } while (mapi_next_result(hdl) == 1);
    mapi_close_handle(hdl);
    mapi_destroy(dbh);
  } else if (dbh != null) {
    mapi_explain(dbh, stderr);
    mapi_destroy(dbh);
  } else {
    fprintf(stderr, "command failed\n");
  }
  exit(-1);
}


MapiHdl query(Mapi dbh, string q) {
  MapiHdl ret = null;
  if ((ret = mapi_query(dbh, toStringz(q) )) == null || mapi_error(dbh) != MOK)
    die(dbh, ret);
  return(ret);
}

void update(Mapi dbh, string q) {
  MapiHdl ret = query(dbh, q);
  if (mapi_close_handle(ret) != MOK)
    die(dbh, ret);
}

void main()
{

auto  dbh = mapi_connect("localhost", 50000, "monetdb", "monetdb", "sql", "voc");
      writeln("DB-connect");
      update(dbh, "CREATE TABLE emp (name VARCHAR(20), age INT)");
      writeln("create");
      update(dbh, "INSERT INTO emp VALUES ('John', 23)");
      update(dbh, "INSERT INTO emp VALUES ('Mary', 22)");
auto hdl = query(dbh, "SELECT * FROM emp");
  while (mapi_fetch_row(hdl)) {
    auto name = mapi_fetch_field(hdl, 0);
    auto age = mapi_fetch_field(hdl, 1);
    printf("%s is %s\n", name, age);
  }
}
```

Will try to make a complete binding available later...
March 22, 2018
On Wednesday, 21 March 2018 at 16:22:45 UTC, Martin Tschierschke wrote:
> Is there an step by step introduction how to convert a C header of an external lib into the right extern(C){} block?

In short, they should be binary compatible: where is a pointer on C side, should be a pointer on D side, same for integer sizes, data layout an calling conventions.
March 22, 2018
On Wednesday, 21 March 2018 at 16:22:45 UTC, Martin Tschierschke wrote:
> Is there an step by step introduction how to convert a C header of an external lib into the right extern(C){} block?

In addition to what others have said, I found the following article on the D Wiki useful:

https://wiki.dlang.org/D_binding_for_C
March 22, 2018
On Thursday, 22 March 2018 at 17:42:46 UTC, Paul Backus wrote:
> On Wednesday, 21 March 2018 at 16:22:45 UTC, Martin Tschierschke wrote:
>> Is there an step by step introduction how to convert a C header of an external lib into the right extern(C){} block?
>
> In addition to what others have said, I found the following article on the D Wiki useful:
>
> https://wiki.dlang.org/D_binding_for_C

Thank you, this one is very good!

« First   ‹ Prev
1 2