Implementing two Vala interfaces in C

About

In Vala you can define interfaces just like in C# and Java. Interfaces imply that you can have class types that implement one or more such interfaces. Vala does not force you to implement its interfaces in Vala. You can also implement them in good-old GObject C.

The sample

The Vala interfaces

test-testone.vala:

namespace Test {
  public interface TestOne {
        public abstract void method_one ();
  }
}

test-testtwo.vala

namespace Test {
  public interface TestTwo {
        public abstract void method_two ();
  }
}

Compiling the Vala interface:

valac -C test-testone.vala
valac -C test-testtwo.vala
# In case you already want to do this:
gcc -c test-testone.c `pkg-config glib-2.0 gobject-2.0 --cflags`
gcc -c test-testtwo.c `pkg-config glib-2.0 gobject-2.0 --cflags`

This will generate you a test-testone.c, test-testone.h, test-testtwo.c and test-testtwo.h file (and also .o files if you did the gcc step).

A test class implemented in GObject/C that implements both interfaces

A test-impl.c file

#include "test-impl.h"

/* The destructor for the instance. 'object' will point to the instance
 * being destructed. */
static void
test_impl_finalize (GObject *object)
{

}

/* Type's constructor. Happens once per type, so once in the entire
 * program. Accidentally the first time the type is ever needed. 
 * Nonetheless it happens at runtime, it's not a compiler thing. */
static void
test_impl_class_init (TestImplClass *class)
{
        GObjectClass *object_class = G_OBJECT_CLASS (class);
        object_class->finalize = test_impl_finalize;
}

/* Like a constructor is this an initializer that happens once 
 * per created instance of TestImpl */
static void 
test_impl_init (TestImpl *self)
{
}

/* Implementation for TestTestOne::method_one in C */
static void 
test_impl_method_one (TestTestOne *self)
{
}

/* Implementation for TestTestOne::method_two in C */
static void 
test_impl_method_two (TestTestTwo *self)
{
}

/* GObject infrastructure for interface Test.TestOne */
static void
test_test_one_iface_init (TestTestOneIface *iface)
{
        iface->method_one = test_impl_method_one;
}

/* GObject infrastructure for interface Test.TestTwo */
static void
test_test_two_iface_init (TestTestTwoIface *iface)
{
        iface->method_two = test_impl_method_two;
}

/* GObject boilerplate macro */
G_DEFINE_TYPE_WITH_CODE (TestImpl, 
        test_impl, 
        G_TYPE_OBJECT,
        G_IMPLEMENT_INTERFACE (TEST_TYPE_TEST_ONE,
                test_test_one_iface_init)
        G_IMPLEMENT_INTERFACE (TEST_TYPE_TEST_TWO,
                test_test_two_iface_init))

A test-impl.h file

#ifndef __TEST_IMPL_H__
#define __TEST_IMPL_H__

#include <glib.h>
#include <glib-object.h>

G_BEGIN_DECLS

#define TEST_TYPE_IMPL (test_impl_get_type ())
#define TEST_IMPL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TEST_TYPE_IMPL, TestImpl))
#define TEST_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_IMPL, TestImplClass))
#define TEST_IS_IMPL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TEST_TYPE_IMPL))
#define TEST_IS_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_IMPL))
#define TEST_IMPL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_IMPL, TestImplClass))

typedef struct _TestImpl TestImpl;
typedef struct _TestImplClass TestImplClass;

struct _TestImpl {
        GObject parent;
};

struct _TestImplClass {
        GObjectClass parent;
};

GType test_impl_get_type (void);
TestImpl *test_impl_new (void); 

G_END_DECLS

#endif /* __TEST_IMPL_H__ */

Generating the .vapi file for the TestImpl class

This will generate a file called test-impl.vapi

 export VALA_PATH=/opt/vala
 $VALA_PATH/lib/vala/gen-introspect --namespace=Test test-impl.h \
        `pkg-config gobject-2.0 --cflags` > test-impl.gi
 $VALA_PATH/bin/vapigen --library test-impl test-impl.gi 

Now change this line in test-impl.vapi:

 public class Impl : GLib.Object {

into

 public class Impl : GLib.Object, TestOne, TestTwo {

If you prefer to manually create the .vapi file:

[CCode (cprefix = "Test", lower_case_cprefix = "test_")]
namespace Test {
        [CCode (cheader_filename = "test-impl.h")]
        public class Impl: GLib.Object, TestOne, TestTwo {
                public weak GLib.Object parent;
                public Impl ();
        }
        [CCode (cheader_filename = "test-impl.h")]
        public class ImplClass {
                public weak GLib.ObjectClass parent;
        }
}

A Makefile.am

INCLUDES = $(PROG_CFLAGS)
bin_PROGRAMS = tester

tester_VALASOURCES = \
        tester.vala \
        test-testone.vala \
        test-testtwo.vala

tester_vapifiles = \
        test-impl.vapi

tester_SOURCES = \
        $(tester_VALASOURCES:.vala=.c) \
        $(tester_VALASOURCES:.vala=.h) \
        test-impl.c \
        test-impl.h

tester.vapi tester.vala.stamp: $(tester_VALASOURCES) $(tester_vapifiles)
        $(VALAC) -C $^ $(tester_vapifiles)
        touch $@

tester_LIBS = $(PROG_LIBS)

EXTRA_DIST = $(tester_vapifiles)

And a little test app in tester.vala

namespace Test {
  public class Application {
        static int main (string[] args) {
                Impl t = new Impl ();
                t.method_one ();
                t.method_two ();
                return 0;
        }
  }
}

Projects/Vala/MultiImplementInC (last edited 2013-11-22 16:48:27 by WilliamJonMcCann)