3

This is my first post, I'm new to this site, but I've been lurking around for a while now. I've got a good knowledge of C and very limited knowledge of C++. I guess. I'm on Windows (XPx64), VS2008.

I'm trying to wrap a C++ library, kdtree2, so that I can use it from C. The main issues relate to accessing the kdtree2 and kdtree2_result_vector classes. As the authors ftp server does not respond I've uploaded a copy of the original distribution kdtree2 src

Just some quick info on the kd-tree (a form of a binary tree), "'the data' are coordinates in n-dimensional Cartesian space and an index. What it is used for are nearest neighbour searches, so after constructing the tree (which will not be modified), one can query the tree for various types of nn-searches. The results in this case are returned in a vector object of structs (c-like structs).

struct kdtree2_result {
  // 
  // the search routines return a (wrapped) vector
  // of these. 
  //
public:
  float dis;  // its square Euclidean distance
  int idx;    // which neighbor was found
}; 

My imagined solution is to have an array of kdtree2 objects (one per thread). For the kdtree2_result_vector class I haven't got a solution yet as I'm not getting past first base. It is not necessary to access the kdtree2 class directly.

I only need to fill it with data and then use it (as the second function below is an example of). For this I've defined:

kdtree2 *global_kdtree2;

extern "C" void new_kdtree2 ( float **data, const int n, const int dim, bool arrange ) {

    multi_array_ref<float,2> kdtree2_data ( ( float * ) &data [ 0 ][ 0 ], extents [ n ][ dim ], c_storage_order ( ) );

    global_kdtree2 = new kdtree2 ( kdtree2_data, arrange );
}

For then using that tree, I've defined:

extern "C" void n_nearest_around_point_kdtree2 ( int idxin, int correltime, int nn ) { 

    kdtree2_result_vector result;

    global_kdtree2->n_nearest_around_point ( idxin, correltime, nn, result );
}

kdtree2_result_vector is derived from the vector class. This compiles without error, and the resulting library can be linked and it's C-functions accessed from C.

The problem is that the invocation of n_nearest_around_point_kdtree2 crashes the program. I suspect somehow between setting up the tree and using it in the second function call, the tree somehow gets freed/destroyed. The calling c-test-program is posted below:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "kdtree2.h"

#define MALLOC_2D(type,x,y) ((type**)malloc_2D_kdtree2((x),(y),sizeof(type)))

void **malloc_2D_kdtree2 ( const int x, const int y, const int type_size ) {

    const int y_type_size = y * type_size;

    void** x_idx = ( void ** ) malloc ( x * ( sizeof ( void ** ) + y_type_size ) );

    if ( x_idx == NULL )
        return NULL;

    char* y_idx = ( char * ) ( x_idx + x );

    for ( int i = 0; i < x; i++ )
        x_idx [ i ] = y_idx + i * y_type_size;

    return x_idx;
}

int main ( void ) {

    float **data = MALLOC_2D ( float, 100, 3 );

    for ( int i = 0; i < 100; i++ )
        for ( int j = 0; j < 3; j++ ) 
            data [ i ][ j ] = ( float ) ( 3 * i + j );

    // this works fine
    tnrp ( data, 100, 3, false );

    new_kdtree2 ( data, 100, 3, false );
    // this crashes the program
    n_nearest_around_point_kdtree2 ( 9, 3, 6 );

    delete_kdtree2 ( );

    free ( data );

    return 0;
}

As far as I can see, searching the internet, it should work, but I'm obviously missing something vital in the brave (for me) new world of C++.

EDIT:

Resolution, thanks to larsmans. I've defined the following class (derived from what larsmans posted earlier):

class kdtree {

private:   

    float **data;
    multi_array_ref<float,2> data_ref;
    kdtree2 tree;

public:

    kdtree2_result_vector result;

    kdtree ( float **data, int n, int dim, bool arrange ) :

        data_ref ( ( float * ) &data [ 0 ][ 0 ], extents [ n ][ dim ], c_storage_order ( ) ),
        tree ( data_ref, arrange )
        {
        }

    void n_nearest_brute_force ( std::vector<float>& qv ) {
        tree.n_nearest_brute_force ( qv, result ); }

    void n_nearest ( std::vector<float>& qv, int nn ) {
        tree.n_nearest ( qv, nn, result ); }

    void n_nearest_around_point ( int idxin, int correltime, int nn ) {
        tree.n_nearest_around_point ( idxin, correltime, nn, result ); }

    void r_nearest ( std::vector<float>& qv, float r2 ) {
        tree.r_nearest ( qv, r2, result ); }

    void r_nearest_around_point ( int idxin, int correltime, float r2 ) {
        tree.r_nearest_around_point ( idxin, correltime, r2, result ); }

    int r_count ( std::vector<float>& qv, float r2 ) {
        return tree.r_count ( qv, r2 ); }

    int r_count_around_point ( int idxin, int correltime, float r2 ) {
        return tree.r_count_around_point ( idxin, correltime, r2 ); }
};

The code to call these functions from C:

kdtree* global_kdtree2 [ 8 ];


extern "C" void new_kdtree2 ( const int thread_id, float **data, const int n, const int dim, bool arrange ) {

    global_kdtree2 [ thread_id ] = new kdtree ( data, n, dim, arrange );
}


extern "C" void delete_kdtree2 ( const int thread_id ) {

    delete global_kdtree2 [ thread_id ];
}


extern "C" void n_nearest_around_point_kdtree2 ( const int thread_id, int idxin, int correltime, int nn, struct kdtree2_result **result ) { 

    global_kdtree2 [ thread_id ]->n_nearest_around_point ( idxin, correltime, nn );

    *result = &( global_kdtree2 [ thread_id ]->result.front ( ) );
}

and eventually the C-program to start using it all:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "kdtree2.h"


int main ( void ) {

    float **data = MALLOC_2D ( float, 100, 3 );

    for ( int i = 0; i < 100; i++ )
        for ( int j = 0; j < 3; j++ ) 
            data [ i ][ j ] = ( float ) ( 3 * i + j );

    int thread_id = 0;

    new_kdtree2 ( thread_id, data, 100, 3, false );

    struct kdtree2_result *result;

    n_nearest_around_point_kdtree2 ( thread_id, 28, 3, 9, &result );

    for ( int i = 0; i < 9; i++ )
        printf ( "result[%d]= (%d,%f)\n", i , result [ i ].idx, result [ i ].dis );

    printf ( "\n" );

    n_nearest_around_point_kdtree2 ( thread_id, 9, 3, 6, &result );

    for ( int i = 0; i < 6; i++ )
        printf ( "result[%d]= (%d,%f)\n", i , result [ i ].idx, result [ i ].dis );

    delete_kdtree2 ( thread_id );

    free ( data );

    return 0;
}
2
  • What's the stack trace for the crash? Commented Mar 6, 2011 at 14:18
  • Seems okay for me... If you could provide a minimal testcase for everyone to reproduce this error, it would be easier to help you Commented Mar 6, 2011 at 14:20

1 Answer 1

3

The API docs in the referenced paper are rather flaky and the author's FTP server doesn't respond, so I can't tell with certainty, but my hunch is that

multi_array_ref<float,2> kdtree2_data((float *)&data[0][0], extents[n][dim],
                                      c_storage_order( ));

global_kdtree2 = new kdtree2(kdtree2_data, arrange);

construct the kdtree2 by storing a reference to kdtree2_data in the global_kdtree2 object, rather than making a full copy. Since kdtree2_data is a local variable, it is destroyed when new_kdtree2 returns. You'll have to keep it alive until n_nearest_around_point_kdtree2 is done.

Sign up to request clarification or add additional context in comments.

6 Comments

@degski: Since you're using a global object, you might want to make the associated data global as well. Make a global copy in the new_kdtree2 function.
The compiler generates: the copy assignment operator for "kdtree2" has been suppressed because member "kdtree2::the_data" has reference type. Therefore it seems it's not possible to copy the whole thing to a global variable (but, I'm swimming here!)...
That actually corroborates my theory. See updated post, in a minute.
@degski: I thought of returning a copy of the data. Never mind the wrapping idea, it gets too complicated. Just copy it to a global multi_array and you should be fine.
@degski: copying the data to someplace global solves the keepalive problem.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.