Utiliser une librairie C/C++ non managée dans un code c# (suite)

Comme nous avons pu le voir dans un article précédent, le wrapping de code C++ en C# peut être très simple, malheureusement les choses peuvent rapidement se compliquer !

En effet, continuons de travailler avec la dll GNU Scientific Library, et essayons d’utiliser une gls_matrix, la déclaration dans la documentation est la suivante :

typedef struct
{
    size_t size1;
    size_t size2;
    size_t tda;
    double * data;
    gsl_block * block;
    int owner;
} gsl_matrix;

La documentation nous explique aussi qu’un gls_block est la structure décrite ci dessous et que l’allocation d’une gsl matrix se fait via la fonction suivante :

typedef struct
{
    size_t size;
    double * data;
} gsl_block;

Function: gsl_matrix * gsl_matrix_alloc (size_t n1, size_t n2)

La première chose à faire est de déclarer une structure de type gsl_block en C#

[StructLayout(LayoutKind.Sequential)]
internal class gsl_block
{
    public uint size = 0;
    public IntPtr data = IntPtr.Zero;
}

On commence par déclarer un type struct et indiquer comment le ‘layout’ doit être effectué puis on déclare la class avec ses attributs. On peut constater la présence d’un type IntPrt , c’est la manière C# de gérer les pointeurs! (vue simplifiée mais bien pratique ^^). On peut maintenant déclarer le type gls_matrix comme suit !

    [StructLayout(LayoutKind.Sequential)]
    internal class gsl_vector
    {
        public uint size = 0;
        public uint stride = 0;
        public IntPtr data = IntPtr.Zero;
        public gsl_block block;
        public int owner = 0;
    }

Il ne reste maintenant plus qu’à compléter la fonction d’allocation gls_matrix_alloc. Là malheureusement il faut vraiment se plonger dans le code de la GSL pour trouver les sources et les adapter. En faisant ça on constate que l’on aura aussi besoin de la fonction gls_block_alloc. Le code des deux fonctions nécessaires sont si dessous :

        internal static gsl_block gsl_block_alloc(uint n)
        {
            gsl_block b = new gsl_block();
            b.data = Marshal.AllocCoTaskMem((int)n * sizeof(double));
            b.size = n;
            return b;
        }

        internal static gsl_matrix gsl_matrix_alloc(uint n1, uint n2)
        {
            gsl_matrix b = new gsl_matrix();
            b.size1 = n1;
            b.size2 = n2;
            b.tda = n2;
            b.block = gsl_block_alloc(n1 * n2);
            b.data = b.block.data;
            b.owner = 1;
            return b;
        }

Le point sensible l’implémentation est de bien comprendre ou est allouée la mémoire, en fait toutes les données sont stockées dans le gls_block c’est donc là qu’il faut alloquer la mémoire, le reste n’est qu’un jeu de pointeur. Bien sur une fois la matrice obtenu vous n’êtes pas encore au bout de vos peines, il faut maintenant coder les fonctions capables de libérer la mémoire allouée :

        internal static void gsl_block_free(gsl_block b)
        {
            if (b != null && b.data != null)
            {
                Marshal.FreeCoTaskMem(b.data);
                b.data = IntPtr.Zero;
            }
            b.size = 0;
        }
        internal static void gsl_matrix_free(gsl_matrix m)
        {
            if (m == null)
                return;
            gsl_block_free(m.block);
            m.block = null;
            m.data = IntPtr.Zero;
            m.size1 = 0;
            m.size2 = 0;
            m.tda = 0;
            m.owner = 0;
        }

Là encore, il faut libérer la mémoire marshallée dans la gsl_block, pour le reste on pourrait se contente de tout remettre à zéro. Dernière étape, allouer et récupérer des valeurs aux éléments de la matrice :

        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_set(gsl_matrix m, uint i, uint j, double x);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_matrix_get(gsl_matrix m, uint i, uint j);

Vous pouvez constater que cette fois je ne définis pas d’entry point dans la dll car le nom C# et le nom C++ sont identiques! Bien sur une gsl_matrix toute seule ne sert pas à grand chose, mais en ajoutant les fonctions de la GSL permettant de faire une décomposition de chloesky ou tout autre opération matricielle …

Les commentaires sont fermés.