简体   繁体   中英

Getting ECL symbol name as string in C++

I'm working on embedding Embeddable Common Lisp into a library and I've been writing utility functions to convert ECL's cl_object to various C/C++ types - eg to convert a cl_object representing a string to a std::string.

My question is this - why am I failing to retrieve a string containing the name of a symbol in ECL?

I'm having trouble getting the following function ecl_symbol_to_string to work, which should take an ECL symbol and return a std::string with it's name:

string ecl_symbol_to_string(cl_object sym) {
    return ecl_string_to_string(sym->symbol.name);
}

string ecl_string_to_string(cl_object echar) {
    string res("");
    int j = echar->string.dim; //get dimension   
    ecl_character* selv = echar->string.self; //get pointer   

    //do simple pointer addition
    for(int i=0;i<j;i++){
        res += (*(selv+i));
    }
    return res;
};

Note that ecl_string_to_string works for lisp strings.

A simple unit test illustrates the failure:

TEST_CASE( "ecl_symbol_to_string returns a string for symbol",
           "[ecl_string_to_string]" ) {

  LispRuntime *rt = new LispRuntime("()");
  std::string eval_script;
  cl_object   eval_result;
  std::string subject_result;

  eval_script = "'mysymbol";
  eval_result = rt->evaluate(eval_script);
  REQUIRE( ECL_SYMBOLP(eval_result) );
  subject_result = ecl_symbol_to_string(eval_result);
  REQUIRE ( ECL_STRINGP(cl_symbol_name(eval_result)) );
  std::cout << subject_result.c_str() << std::endl;
  REQUIRE( subject_result.compare("mysymbol") == 0 );

  delete rt;

}

This test case prints out MM for the call to cout. I have also tried comparing to "MYSYMBOL", which fails, and "M" which passes.

LispRuntime::eval_script simply converts and evaluates the form:

cl_object LispRuntime::evaluate(std::string &code) {
  cl_object form = c_string_to_object(code.c_str());
  cl_object result = cl_eval(form);

  return result;
}

I compiled ECL version 16.1.3 locally with the C++ option enabled, with debugging symbols and with all other settings default. Any assistance is greatly appreciated.

I believe this is a unicode/non-unicode mix-up: ECL defines two string types in object.h . One is ecl_base_string , where the member self ultimately typedefs to an unsigned char* and the other is ecl_string where the member self usually (depending on compile time arguments, I think) typedefs to a int* . You're accessing it as an ecl_string .

If you trace through the workings of ecl_make_symbol you find it ends up calling the function make_constant_base_string which returns a base string. Thus your ecl_string_to_string accesses it through the wrong type.

I suspect the easiest solution is to build a type-check/conversion into ecl_string_to_string :

string ecl_string_to_string(cl_object echar) {
    switch (ecl_t_of(echar)) {
    #ifdef ECL_UNICODE
      case t_string:
        if (!ecl_fits_in_base_string(echar)) {
          echar = cl_copy_seq(echar);
        } else {
          echar = si_copy_to_simple_base_string(echar);
        }
        break;
    #endif
      case t_base_string:
        // OK
        break;
      default:
        // PRINT SOME ERROR
        return string(); // or raise an exception
    }

    string res("");
    int j = echar->base_string.dim; //get dimension   
    ecl_base_char* selv = echar->base_string.self; //get pointer   

    //do simple pointer addition
    for(int i=0;i<j;i++){
        res += (*(selv+i));
    }
    return res;
};

The extra code I've added was heavily copied from the ECL function cl_make_symbol . I decided to convert to ecl_base_string rather than ecl_string since the C++ string won't accept unicode characters anyway. You could probably do it the other way round if you had good reason to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM