Native extensions - C++ native libraries
Asked Answered
E

2

5

The samples_extension works OK with C library linkage but what about C++ libraries?

I have a class based C++ library I want to use as a native extension, so we have for example:-

class Connect {
      open(...);
  ....
}

in C++ and I want a similar class in Dart.

Looking at dart_api.h and dart_native_api.h its not clear to me how I pass class pointers back and forth from C++ to Dart and how I invoke methods on them and tie this back to the Dart class instance. How does ResolveName work with connection->open() type calls or do we do this completely differently.

Encore answered 16/1, 2014 at 7:54 Comment(0)
E
3

OK, a bit of digging and I've sussed this now, I'm doing this in Dart :-

bool open() native 'Connection::open';

and in my resolver looking for the string 'Connection::open' then calling a native function. So, this means my native functions are named 'connectionOpen' and 'messageOpen' etc. so I can resolve these.

Encore answered 16/1, 2014 at 11:52 Comment(0)
B
6

Basic project that can get answers to your questions:

cpp_extension.cc

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#ifdef _WIN32
#include "windows.h"
#else
#include <stdbool.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/mman.h>
#endif

#include "dart_api.h"

Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool* auto_setup_scope);

DART_EXPORT Dart_Handle cpp_extension_Init(Dart_Handle parent_library) {
  if (Dart_IsError(parent_library)) { return parent_library; }

  Dart_Handle result_code = Dart_SetNativeResolver(parent_library, ResolveName);
  if (Dart_IsError(result_code)) return result_code;

  return Dart_Null();
}

Dart_Handle HandleError(Dart_Handle handle) {
  if (Dart_IsError(handle)) Dart_PropagateError(handle);
  return handle;
}

class Connection {
  int* buffer;
  bool opened;

  public:
    Connection() {
      opened = false;
      buffer = new int[1000000];
      memset(buffer, 1, 1000000);
    }

    void close() {
      opened = false;
    } 

    void open(const char* connectionString) {
      opened = true;
    }

    ~Connection() {
      delete buffer;
    }
};

void ConnectionClose(Dart_NativeArguments arguments) {
  Connection* connection;
  Dart_Handle dh_handle;

  Dart_EnterScope();
  dh_handle = Dart_GetNativeArgument(arguments, 0);
  connection = (Connection*)dh_handle;
  connection->close();
  Dart_Handle result = Dart_Null();
  Dart_SetReturnValue(arguments, result);
  Dart_ExitScope();
}

void ConnectionCreate(Dart_NativeArguments arguments) {  
  Connection* connection;
  Dart_Handle result;

  Dart_EnterScope();
  connection = new Connection();
  result = Dart_NewInteger((int64_t)connection);
  Dart_SetReturnValue(arguments, result);
  Dart_ExitScope();
}

void ConnectionPeerFinalizer(Dart_WeakPersistentHandle handle, void *peer) {
  delete (Connection*) peer;
}

void ConnectionPeerRegister(Dart_NativeArguments arguments) {
  int64_t peer;
  Dart_Handle dh_object;
  Dart_Handle dh_peer;

  Dart_EnterScope();
  dh_object = Dart_GetNativeArgument(arguments, 0);
  dh_peer = Dart_GetNativeArgument(arguments, 1);
  Dart_IntegerToInt64(dh_peer, &peer);
  Dart_NewWeakPersistentHandle(dh_object, (void*)peer, ConnectionPeerFinalizer);
  Dart_SetReturnValue(arguments, Dart_Null());
  Dart_ExitScope();
}

void ConnectionOpen(Dart_NativeArguments arguments) { 
  Connection* connection;
  const char* connectionString;
  Dart_Handle dh_connectionString;
  Dart_Handle dh_handle;

  Dart_EnterScope();
  dh_handle = Dart_GetNativeArgument(arguments, 0);
  dh_connectionString = Dart_GetNativeArgument(arguments, 1);
  Dart_StringToCString(dh_connectionString, &connectionString);
  connection = (Connection*)dh_handle;
  connection->open(connectionString);  
  Dart_Handle result = Dart_Null();
  Dart_SetReturnValue(arguments, result);
  Dart_ExitScope();
}

struct FunctionLookup {
  const char* name;
  Dart_NativeFunction function;
};

FunctionLookup function_list[] = {
  {"ConnectionClose", ConnectionClose},
  {"ConnectionCreate", ConnectionCreate},
  {"ConnectionOpen", ConnectionOpen},
  {"ConnectionPeerRegister", ConnectionPeerRegister},
  {NULL, NULL}};

Dart_NativeFunction ResolveName(Dart_Handle name, int argc, bool* auto_setup_scope) {
  if (!Dart_IsString(name)) return NULL;
  Dart_NativeFunction result = NULL;
  Dart_EnterScope();
  const char* cname;
  HandleError(Dart_StringToCString(name, &cname));

  for (int i=0; function_list[i].name != NULL; ++i) {
    if (strcmp(function_list[i].name, cname) == 0) {
      result = function_list[i].function;
      break;
    }
  }
  Dart_ExitScope();
  return result;
}

cpp_extension.dart

library dart_and_cpp_classes.ext_cpp_extension;

import "dart-ext:cpp_extension";

class Connection {
  final String connectionString;

  int _handle;

  bool _opened = false;

  Connection(this.connectionString) {
    _handle = _create();
    _peerRegister(this, _handle);
  }

  bool get opened => _opened;

  void close() {
    _close(_handle);
    _opened = false;
  }

  void open() {
    _open(_handle, connectionString);
    _opened = true;
  }

  int _create() native "ConnectionCreate";

  void _close(int handle) native "ConnectionClose";

  void _open(int handle, String connectionString) native "ConnectionOpen";

  void _peerRegister(Object object, int handle) native "ConnectionPeerRegister";
}

use_cpp_extension.dart

import 'package:dart_and_cpp_classes/cpp_extension.dart';

void main() {
  var count = 500;
  var connections = [];
  for(var i = 0; i < count; i++) {
    var connection = new Connection("MYSQL");
    connection.open();
    connection.close();
    connections.add(connection);
  }

  connections = null;
  print("Done");
}

Here is basic (ready to use) package on github: https://github.com/mezoni/dart_and_cpp_classes

Other required files in this package.

Run:

  • bin/build_cpp_extension.dart
  • bin/use_cpp_extension.dart

P.S.

I am not C++ programmer.

Please, forgive me for any inaccuracies in this language.

Butterwort answered 16/1, 2014 at 8:41 Comment(1)
Yes I know this, I want to do this in the extension library 'myclass->getVersionString()' not a static call to getVersionString(), so I need to tie the incoming Dart object to my instantiated class on the native side. I think I need to use weakpersistenthandles and the Dart_Invoke API call, what I'm unsure about is how to resolve this in Dart 'bool open(String url) native 'Connect::open';' to a named function using ResolveName.Encore
E
3

OK, a bit of digging and I've sussed this now, I'm doing this in Dart :-

bool open() native 'Connection::open';

and in my resolver looking for the string 'Connection::open' then calling a native function. So, this means my native functions are named 'connectionOpen' and 'messageOpen' etc. so I can resolve these.

Encore answered 16/1, 2014 at 11:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.