Search code examples
kotlinkotlin-multiplatformkotlin-native

How to cast Kotlin interface to a class which implements it in kotlin-native dynamic library?


I am trying to implement kotlin-native dynamic library and use it in c++ code. For example, I have the following code, a function that creates an instance of ResponseImpl and returns interface Response

interface Response
    
class ResponseImpl(val text: String) : Response
    
fun getResponse(): Response = ResponseImpl(text = "This is the ResponseImpl instance")    

The task ./gradlew :shared:linkReleaseSharedMacosX64 generates for me this lines in header file:

  struct {
    struct {
      struct {
        struct {
          struct {
            libNativeTest_kref_com_test_nativesample_Response (*getResponse)();
            struct {
              libNativeTest_KType* (*_type)(void);
            } Response;
            struct {
              libNativeTest_KType* (*_type)(void);
              libNativeTest_kref_com_test_nativesample_ResponseImpl (*ResponseImpl)(const char* text);
              const char* (*get_text)(libNativeTest_kref_com_test_nativesample_ResponseImpl thiz);
            } ResponseImpl;
          } nativesample;
        } test;
      } com;
    } root;
  } kotlin;
} libNativeTest_ExportedSymbols;

In c++ code I call getResponse() function and try to access text property. isInstance is always true but the text property is always null.

#include <iostream>
#include "libNativeTest_api.h"

int main() {
    auto lib = libNativeTest_symbols();
    auto packet = lib->kotlin.root.com.test.nativesample;

    auto response = packet.getResponse();

    bool isInstance = lib->IsInstance(response.pinned, packet.ResponseImpl._type());

    if (isInstance) {
        auto casted_response = (libNativeTest_kref_com_test_nativesample_ResponseImpl *) response.pinned;
        auto text = packet.ResponseImpl.get_text(*casted_response);
        if (text == nullptr) {
            std::cout << "TEXT IS NULL" << std::endl;
        } else {
            std::cout << text << std::endl;
        }
    }
    return 0;
}

What is the proper way to cast Kotlin interface to its implementation class to access the details?


Solution

  • // Response.kt
    // just onto root package
    interface Response
    
    class ResponseImpl(val text: String) : Response
    
    fun getResponse(): Response = ResponseImpl(text = "This is the ResponseImpl instance")
    

    No need to use pinned, where pinned cannot be cast to (libNativeTest_kref_com_test_nativesample_ResponseImpl *)

    // main.c
    #include <stdio.h>
    #include "libnative_api.h"
    
    int main(int argc, char **argv) {
        libnative_ExportedSymbols *symbols = libnative_symbols();
        libnative_kref_Response response = symbols->kotlin.root.getResponse();
        libnative_kref_ResponseImpl *responseImpl = (libnative_kref_ResponseImpl*)&response;
        printf("Response: %s\n", symbols->kotlin.root.ResponseImpl.get_text(*responseImpl));
        return 0;
    }
    
    //output
    Response: This is the ResponseImpl instance