c++schemechibi-scheme

Out of bounds index returns correct values from vector created in chibi scheme


I've embedded chibi scheme into my C++ application and am trying to create a float vector with a size of 3 in scheme, and then get the individual values of that vector back into my c++ program, however when I attempt to do so I only get the correct results if I use indexes beyond the size of the vector. As you can see below I verify the size of the vector.

test.scm

(define (test-vec in-a in-b)
  (let ((vec (vector (* 3.1 in-a) (* 4.1 in-b) 5.0)))
    (display "From Scheme: ")
    (display vec)
    (display "\n")
    vec))

test.cpp

#include <iostream>
#include <chibi/eval.h>

int is_defined(sexp ctx, const char *sym)
{
    sexp_gc_var1(ret);
    sexp_gc_preserve1(ctx, ret);

    ret = sexp_eval_string(ctx, sym, -1, NULL);

    int defined = sexp_procedurep(ret);

    sexp_gc_release1(ctx);

    return defined;
}

int main()
{
    float returnA, returnB, returnC, returnD, returnE, returnF;
    sexp_scheme_init();
    sexp ctx = sexp_make_eval_context(NULL, NULL, NULL, 0, 0);
    sexp_load_standard_env(ctx, NULL, SEXP_SEVEN);
    sexp_load_standard_ports(ctx, NULL, stdin, stdout, stderr, 1);

    // Load the scheme file and create temp variables to pass the values to chibi
    sexp_gc_var6(inAVal, inASym, inBVal, inBSym, returnedVector, filePath);
    sexp_gc_preserve6(ctx, inAVal, inASym, inBVal, inBSym, returnedVector, filePath);

    filePath = sexp_c_string(ctx, "test.scm", -1);
    sexp_load(ctx, filePath, NULL);

    // Ensure our procedure is defined
    if(is_defined(ctx, "test-vec"))
        std::cout << "test-vec is defined" << std::endl;

    // Create the values and create the symbols
    inAVal = sexp_make_flonum(ctx, 1.0);
    inASym = sexp_intern(ctx, "a", -1);

    inBVal = sexp_make_flonum(ctx, 2.0);
    inBSym = sexp_intern(ctx, "b", -1);

    // Bind the values to the symbols and pass them to chibi
    sexp_env_define(ctx, sexp_context_env(ctx), inASym, inAVal);
    sexp_env_define(ctx, sexp_context_env(ctx), inBSym, inBVal);
    // Evaluate the expression and store the result
    returnedVector = sexp_eval_string(ctx, "(test-vec a b)", -1, NULL);
    std::cout << "Vector size: " << sexp_vector_length(returnedVector) << std::endl;

    // I would expect this to return the expected results?
    returnA = sexp_flonum_value(sexp_vector_ref(returnedVector, 0));
    returnB = sexp_flonum_value(sexp_vector_ref(returnedVector, 1));
    returnC = sexp_flonum_value(sexp_vector_ref(returnedVector, 2));
    std::cout << "Vector[0] = " << returnA << "\nVector[1] = " << returnB << "\nVector[2] = " << returnC << std::endl << std::endl;

    // If I index outside of the range of the vector, it gives me the correct results?
    returnD = sexp_flonum_value(sexp_vector_ref(returnedVector, 0));
    returnE = sexp_flonum_value(sexp_vector_ref(returnedVector, 3));
    returnF = sexp_flonum_value(sexp_vector_ref(returnedVector, 4));
    std::cout << "Vector[0] = " << returnD << "\nVector[3] = " << returnE << "\nVector[4] = " << returnF << std::endl;

    sexp_gc_release6(ctx);
}

Which gives me the output:

test-vec is defined
From Scheme: #(3.1 8.2 5.0)
Vector size: 3
Vector[0] = 3.1
Vector[1] = 3.1
Vector[2] = 8.2

Vector[0] = 3.1
Vector[3] = 8.2
Vector[4] = 5

How come indexing beyond the length of the vector is giving me the correct values?


Solution

  • Figured out the issue. It's because sexp_vector_ref(vec, i) evaluates to

    #define sexp_vector_ref(x,i)   (sexp_vector_data(x)[sexp_unbox_fixnum(i)])
    

    and sexp_unbox_fixnum(i) evaluates to

    #define sexp_unbox_fixnum(n)   (((sexp_sint_t)((sexp_uint_t)(n) & ~SEXP_FIXNUM_TAG))/(sexp_sint_t)((sexp_sint_t)1<<SEXP_FIXNUM_BITS))
    

    Which when we just pass in straight integers

    std::cout << "Unboxed fixnums: " << 
    sexp_unbox_fixnum(0) << " " << 
    sexp_unbox_fixnum(1) << " " << 
    sexp_unbox_fixnum(2) << " " << 
    sexp_unbox_fixnum(3) << " " << 
    sexp_unbox_fixnum(4) << std::endl;
    

    gives us the output Unboxed fixnums: 0 0 1 1 2

    However if we first convert them into the proper types that chibi expects, like so

    std::cout << "Fixnum: " <<
        sexp_unbox_fixnum(sexp_make_fixnum(0)) << " " <<
        sexp_unbox_fixnum(sexp_make_fixnum(1)) << " " <<
        sexp_unbox_fixnum(sexp_make_fixnum(2)) << " " <<
        sexp_unbox_fixnum(sexp_make_fixnum(3)) << " " <<
        sexp_unbox_fixnum(sexp_make_fixnum(4)) << std::endl;
    

    we get the proper output

    Fixnum: 0 1 2 3 4
    

    Solution: Use the correct types when indexing