user2485710
user2485710

Reputation: 9811

Any chance I can keep type-safety and memory abstraction with Vala and C++?

My problem is how to interface C++ with Vala, a really basic and fundamental problem for me, especially when it comes to memory, modifying data structures and type safety.

The usual answers are about things like :

The problem is that C it's not even close to what C++ can offer, and it's just different language 90% of the time; on the other hand the problem with Glib is that it's not standard and it's not C++ either and I have a lot of code in C++ that I would like to reuse in a safe way, plus coding in C++ is way more effective for me.

So what happens when I want to pass an std::vector<T> to Vala or a stringstream ? What happens when my Vala program wants to copy, reference, edit and resize/relocate that container ?

I don't know how Vala handles this because vapi and the bottom line of Vala is about C, and I can't find any specific answer about how to deal with C++ code.

Considering that I already discarded GTKmm as a real option, How do you suggest I should go with Vala & C++ ?

Upvotes: 1

Views: 176

Answers (1)

nemequ
nemequ

Reputation: 17522

Vala does not support C++. The reason you can't find an answer about how to deal with C++ code is that there isn't one--you don't.

The only way to get a C++ library to work from Vala is to write a wrapper in C first. That wrapper can use GLib if you want--Vala requires GLib anyways so if you're planning on Vala software being the sole consumer of the C API using GLib doesn't really add a dependency. Or you can write C bindings for std::vector or stringstream.

If you want examples of C wrappers for C++ libraries, take a look at Snappy or LevelDB. They are both pretty good examples of how easy it is to create a simple C wrapper, though IMHO neither is particularly good from an API perspective (actually, I don't think the C++ APIs are particularly good, but I feel that way about most C++ APIs...).

You're not going to get type safety for C++ templates in a C API if you want to retain the ability to handle all types. You're going to have to use void* (or something similar, like gpointer) in the C API, but you can get the type safety back in Vala with the right bindings. For example, if I wanted to create a binding for std::vector, part of the C API might look like

/* foo-vector.h */
#ifdef __cplusplus
extern "C" {
#endif

typedef void (*FooDestroyNotify) (void* data);
typedef struct _FooVector FooVector;

FooVector* foo_vector_new (FooDestroyNotify destroy_notify);
void foo_vector_free (FooVector* vec);
void foo_vector_push_back (FooVector* vec, void* value);
void* foo_vector_pop_back (FooVector* vec);

#ifdef __cplusplus
}
#endif

And

/* foo-vector.cpp */

#include <iostream>
#include <vector>

#include <stdlib.h>

struct _FooVector {
    std::vector<void*>* vec;
    FooDestroyNotify destroy_notify;
};

FooVector* foo_vector_new (FooDestroyNotify destroy_notify) {
    FooVector* vec = (FooVector*) malloc (sizeof (FooVector));

    vec->vec = new std::vector<void*>;
    vec->destroy_notify = destroy_notify;

    return vec;
}

void foo_vector_push_back (FooVector* vec, void* value) {
    vec->vec->push_back (value);
}

void* foo_vector_pop_back (FooVector* vec) {
    void* res = NULL;
    if (!vec->vec->empty ()) {
        res = vec->vec->back ();
        vec->vec->pop_back ();
    }
    return res;
}

void foo_vector_free (FooVector* vec) {
    if (vec->destroy_notify != NULL) {
        std::vector<void*>::iterator it = vec->vec->begin ();

        for (; it < vec->vec->end () ; it++) {
            vec->destroy_notify (*it);
        }
    }

    delete vec->vec;
    free (vec);
}

(If you don't want to use glib you can just create a similar typedef instead of using GDestroyNotify.)

Then, in a VAPI

[Compact]
public class Vector<T> {
    [CCode (simple_generics = true)]
    public Vector ();
    public void push_back (owned T value);
    public owned T pop_back ();
}

The problem with this is that it only works with types which can be stuffed in a pointer. From the C side this is basically how things are done, but if you need to bind something like std::vector you'll need to either create a specialized binding for that one type or model your API a bit like GArray, where you have to pass around sizes (which will not be fun to use from Vala).

Upvotes: 4

Related Questions