Search Unity

Making a plugin with access to GMP library

Discussion in 'Scripting' started by klindeman, Oct 6, 2005.

  1. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Ok, I had been talking in the IRC channel about this for a couple of days now, and David suggest I post a simple example of what I am doing. It does have one problem with it I am not sure of how to get working, but by the end it should be kind of cool.

    I needed a plugin to do very large math operations, like 16 digit numbers multiplied by 8 digit numbers. To do this, I found a library called GMP.

    Here is the C plugin end of it. This just takes in a number and does some multiplication on it, then returns it as a char (since char is the only thing large enough that can hold the number, and what GMP works with).

    Code (csharp):
    1. char* doMultiplication (const char* string)
    2. {
    3.     mpz_t a, b, p;
    4.    
    5.     mpz_init (a);
    6.     mpz_init (b);
    7.     mpz_init (p);
    8.    
    9.     mpz_set_str (a, string, 10);
    10.     mpz_set_str (b, "449743124546", 10);
    11.    
    12.     mpz_mul (p, a, b);
    13.     char* finalNum;
    14.    
    15.     mpz_get_str(finalNum , 10, p);
    16.        
    17.     mpz_clear (a);
    18.     mpz_clear (b);
    19.     mpz_clear (p);
    20.     return finalNum;
    21. }
    Now in a regular C Console app, I can printf finalNum and it outputs the correct number(string). Now I build the plugin, and load it in a C# script like this:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Runtime.InteropServices;
    4.  
    5. public class GMPTest : MonoBehaviour
    6. {
    7.    [DllImport ("GMP")]
    8.    private static extern string doMultiplication (string myString);
    9.  
    10.    void Start ()
    11.    {
    12.       string output = "";
    13.       output = doMultiplication("45649874654987");
    14.    }
    15. }
    Now this is where the problem lies. I think I am doing something wrong for when it returns the string. When I run this Unity pops out the error "System.NullReferenceException: Object reference not set to an instance of an object" and highlights the last } on the Start function. And yes, the file GMP.bundle is in Assets/Plugins.

    Any ideas?
     
  2. David-Helgason

    David-Helgason

    Moderator

    Joined:
    Mar 29, 2005
    Posts:
    1,104
  3. freyr

    freyr

    Joined:
    Apr 7, 2005
    Posts:
    1,148
    try adding a printf in the C++ side before you return.

    Something like
    Code (csharp):
    1. printf("The return value is: %s\n", finalNum);
    Then take a look in the console log to see what it prints. You might have to do some memory allocation on the C++ side.
     
  4. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Adding that printf to the plugin sure made Unity crash real good. I tried it twice, same effect both times.

    Here is a link to the crash log
     
  5. freyr

    freyr

    Joined:
    Apr 7, 2005
    Posts:
    1,148
    Looks like you need to allocate space for finalNum before passing it to GMP.

    The last point of control before crashing inside GMP is in multiplyScore.c at line 30. Is that line the 'mpz_get_str' line in your example code by any chance?
     
  6. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Hmm.

    It is actually the mpz_clear (a); line. I was able to do this in a separate, non .bundle version project, and it seemed to return the value fine.
     
  7. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Ok, mucked around a little bit and got it working. It was pretty much what you said it seems. Here is the source that works:

    Code (csharp):
    1. char* doMultiplication (const char* string)
    2. {
    3.    mpz_t a, b, p;
    4.    
    5.    mpz_init (a);
    6.    mpz_init (b);
    7.    mpz_init (p);
    8.    
    9.    mpz_set_str (a, string, 10);
    10.    mpz_set_str (b, "449743124546", 10);
    11.    
    12.    mpz_mul (p, a, b);
    13.    char* finalNum[100];
    14.    
    15.    mpz_get_str(*finalNum , 10, p);
    16.        
    17.    mpz_clear (a);
    18.    mpz_clear (b);
    19.    mpz_clear (p);
    20.    return *finalNum;
    21. }
     
  8. freyr

    freyr

    Joined:
    Apr 7, 2005
    Posts:
    1,148
    That last one seems a bit scary to me.

    The line saying
    Code (csharp):
    1. char* finalNum[100]
    allocates 100 uninitialised char pointers. Not a single char string of 100 characters. If you wanted finalNum to be a string with room for 100 chars it should be
    Code (csharp):
    1. char finalNum[100]
    but even that won't work as the 100 chars are allocated on the stack frame of the current function. As soon as it returns, the char* returned will no longer be valid.

    If you do not need the function to be thread safe you could declare finalNum to be static like this:
    Code (csharp):
    1. static char finalNum[100]
    That way the same memory buffer will be reused every time. This will be okay, since the plugin interface will copy its contents when automatically converting the char* to a .Net string.

    But that is not good either, as you will be assuming the number is max 99 characters long. (C strings have to have room for a terminating null byte.)
    You can call
    Code (csharp):
    1. mpz_sizeinbase (p,10)
    to see how much room is needed. (Add 2 bytes to get room for the zero byte and stuff.)

    This leaves us the problem on how to allocate the correct amount of memory without leaking.
    Using the static approach we could do the following:
    Code (csharp):
    1.  
    2.    static int allocated_size=0;
    3.    static char* finalNum=NULL;
    4.  
    5.    int size=mpz_sizeinbase (p,10)+2;
    6.    if(size > allocated_size) {
    7.       if(finalNum != NULL) free(finalNum);
    8.       finalNum=malloc(size);
    9.       allocated_size=size;
    10.    }
    11.  
     
  9. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Doh, I totally didn't think about that. I shall play around with that.
     
  10. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Hmm, I am getting an error I never had with malloc before in XCode. When building as a plugin it is fine, but when I build as a console app to test first, it conplains about the finalNum=malloc(size); line, saying "invalid conversion from void* to char*". I am sure I have used it like that before (or at least close to that).

    Edit: Actually, the invalid conversion thing was actually because I put it in a C++ file, oops :/ Works as a C file. As a C++ file I should have tried new/delete instead of malloc/free.