Использование map в C++ и предоставление функций-оболочек для использования в C

В MSVC6 мне нужно использовать map в C++ и предоставлять функции-оболочки, чтобы использовать их в c только для добавления, удаления и получения размера. Я хочу сделать это без использования каких-либо классов в C++. Это будет использоваться потоками, каждый поток будет иметь дескриптор структуры, который будет иметь void * который будет передан в качестве параметра в функции оболочки C++, и я хочу, чтобы код C++ преобразовал это void * на карту и делать операции. Я не могу конвертировать void * к карте в C++ было бы полезно любое решение.

2 ответа

Я попробую, потому что мне нравится гадать:

Я предполагаю, что у вас есть функция, которая передается в функцию create thead, которая выглядит примерно так:

int callback(void* userdata);

Итак, давайте сделаем функцию обратного вызова для доступа к карте:

// In the header file: "my_callback.h" we first need to declare that this function will be used from a C file, this is so the compiler will name the function according to the C standard and not C++.
#ifdef __cplusplus
extern "C" {
#endif

// Declare your function:
int my_callback(void * map_handle);

#ifdef __cplusplus
extern "C" {
#endif

Теперь, в файле C++, убедитесь, что вы включили заголовочный файл:

#include "my_callback.h"

int my_callback(void * map_handle) {
    // First, for convenience, let us define the map type
    using mapType = std::map<std::string, int>;

    // First cast the void * to a the correct map * pointer
    mapType *mapPtr = static_cast<mapType*>(map_handle);

    mapPtr->insert( "Test", 1 ); // Insert
    cout << (*mapPtr)["Test"];   // read

    // Or you can assign it to a reference to hide away the pointer syntax:
    mapType & mapRef = *static_cast<mapType*>(map_handle);


    mapRef.insert( "Test", 1 ); // Insert
    cout << mapRef["Test"];   // read

    // (...)
}

Есть несколько вещей, которые стоит отметить:

  1. Нам разрешено использовать static_cast потому что вход является void Указатель, вы должны передавать адрес целевой карты, когда вы создаете поток, и статическое приведение к и затем из пустого указателя на тот же тип хорошо определено.

  2. Это никоим образом не потокобезопасно. При обращении к одному и тому же ресурсу из нескольких потоков следует соблюдать особую осторожность. Если вы намереваетесь иметь только одну карту и разрешить всем потокам обращаться к ней, вы должны включить мьютекс, чтобы гарантировать, что только один поток обращается к ней одновременно. Отличный способ сделать это - просто создать небольшую структуру, включающую карту и мьютекс:

    struct map_n_mutex {mapType map; some_mutex_t mutex; // Не уверен, какой будет правильный тип мьютекса в Windows. };

На этапе инициализации вашего потока:

// Create map
map_n_mutex user_data;

// Create thread (I'm making this function up, use what you have been using)
create_thread( my_callback, &user_data); // pass a pointer to userdata

Если вы придерживаетесь этого подхода, помните, что вам нужно map_n_mutex и не mapType внутри функции обратного вызова.

Код C нужно будет получить void указатель из кода C++, делает что-то вроде

void *get_map_from_cpp();   /* declare the function */

void *our_map = get_map_from_cpp();

где get_map_from_cpp() будет определен (в C++) как что-то вроде;

 #include <map>
 #include <string>

 extern "C" void *get_map_from_cpp()
 {
      std::map<std::string, float> *the_map = new std::map<std::string, float>;
      return static_cast<void *>(the_map);
 }

Это не останавливается там все же. Чтобы вставить значения в карту, нам нужно передать значения, например, в C

  void insert_to_map(void *, const char *str, float f);   /* declaration */

  insert_to_map(our_map, "Everything", 42.0);

где insert_to_map() также должен быть определен в C++, например как

   extern "C" void insert_to_map(void *m, const char *str, float f)
   {
        std::map<std::string, float> *the_map;

        the_map = static_cast< std::map<std::string, float> *>(m);
        std::string the_str(str);
        (*the_map)[the_str] = f;
   }

Точно так же, retrieve_from_map() может быть реализовано как

 extern "C" float retrieve_from_map(void *m, const char *str)
 {
      std::map<std::string, float> *the_map;

      the_map = static_cast< std::map<std::string, float> *>(m);
      std::string the_str(str);

      std::map<std::string, float>::const_iterator i = the_map->find(the_str);
      if (i != the_map->end())
          return i->second;
      else
          return 0.0f;     // string not found in map, so return 0.0
 }

Функции, вызываемые из C, должны предоставлять интерфейс только для C - это означает, что код C не может напрямую касаться типов C++. Во-вторых, сопоставление с C++ должно выполняться исключительно в коде C++, поскольку компилятор C не будет понимать конструкции. Это означает, что функции должны принимать или возвращать только те типы, которые имеют смысл в C, функции должны быть объявлены как extern "C" для компилятора C++ (чтобы их можно было вызывать из C), а тело функции должно обрабатывать отображение типов C в C++.

Это зависит от совместимости компиляторов C и C++ (например, от одного поставщика, совместимого ABI и т. Д.).

Другие вопросы по тегам