вызов C++ кода из C

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

вызов C++ кода из C

Непрочитанное сообщение Olej » 10 мар 2017, 14:42

Конкретная задача:
- код достаточно обстоятельной задачи, из области компьютерного зрения
- используется OpenCV
- в OpenCV есть API C & API C++ ... но API C++ намного богаче, а задача достаточно сложная...
- но и это не главное, в задаче очень много динамических объектов непредсказуемой размерности ... хотелось бы использовать STL
- вызывать хотелось бы из C-кода, 1 (2,3,...) достаточно простых итоговых вызова ... boolean признак "да/нет" на предмет распознавания некоторого образа
- и поместить этот C-код в DLL библиотеку для использования её в бэк-энде какого-то там WEB проекта.

Здесь вопрос связывания, линковки, приведение в соответствие соглашений вызовов C/C++ и заталкивание всего этого (и C++ и C) в shared DLL.

Задача чисто техническая...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: вызов C++ кода из C

Непрочитанное сообщение Olej » 10 мар 2017, 21:01

Olej писал(а):Задача чисто техническая...
Схема такая:

1. Код С++ в библиотеке DLL (файл child.cc):

Код: Выделить всё

#include "common.h"

int debug_level = 2;

bool predicat( test_t *t ) {
   return t->level == debug_level;
}

__attribute__ ((destructor)) static void on_exit( void ) {
   cout << "завершение: подчистка перед выгрузкой" << endl;
}
2. Общий файл определений (common.h) который делает объекты DLL доступными вызывающим программам (вот как-раз с ним то и пришлось повозиться ;-) ):

Код: Выделить всё

#ifndef CHILD_H
#define CHILD_H 

#include <stdlib.h>

extern int debug_level;

typedef struct  {       // class test
   char s[ 77 ];
   int level;
} test_t;

#ifdef __cplusplus
#include <iostream>
using namespace std;
extern "C" {
#else
#include <stdbool.h>
#include <stdio.h>
#endif

bool predicat( test_t* );

#ifdef __cplusplus
}
#endif

#endif                  // CHILD_H
3. вызывающий (DLL) процесс на языке C (файл predicat.c):

Код: Выделить всё

#include "common.h"

static void on_exit( void ) {
   printf( "завершение процесса\n" );
}

int main( int argc, char *argv[] ) {
   atexit( on_exit );
   test_t t = { "", 2 };
   printf( "результат: %s\n", predicat( &t ) ? "TRUE" : "FALSE" );
   debug_level = 3;
   printf( "результат: %s\n", predicat( &t ) ? "TRUE" : "FALSE" );
   return 0;
}
4. вызывающий (ту же DLL) процесс, но теперь уже на языке C++ (файл predicat.cc):

Код: Выделить всё

#include "common.h"

static void on_exit( void ) {
   printf( "завершение процесса\n" );
}

int main( int argc, char *argv[] ) {
   atexit( on_exit );
   test_t t = { "", 2 };
   cout << "результат: " << ( predicat( &t ) ? "TRUE" : "FALSE" ) << endl;
   debug_level = 3;
   cout << "результат: " << ( predicat( &t ) ? "TRUE" : "FALSE" ) << endl;
}

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: вызов C++ кода из C

Непрочитанное сообщение Olej » 10 мар 2017, 21:09

Olej писал(а): Схема такая:
Теперь собираем всё это вместе, Makefile :

Код: Выделить всё

TARGET = proxi
CHILD = child
TARGETM = $(TARGET)m
TARGETA = $(TARGET)a
TARGETP = $(TARGET)p

LIB = lib$(TARGET).so

CC  += -Wall -std=c99 -O3
CXX += -Wall -std=c++11 -O3

all: $(LIB) $(TARGETM) $(TARGETA) $(TARGETP)

$(LIB): $(CHILD).cc common.h
        $(CXX) -c -fpic -fPIC -shared $(CHILD).cc -o $(CHILD).o
        $(CXX) -shared -o $(LIB) $(CHILD).o
        rm -f *.o

$(TARGETM): $(TARGET).c $(CHILD).cc common.h
        $(CXX) -c $(CHILD).cc -o $(CHILD).o
        $(CC) -c $(TARGET).c -o $(TARGET).o
        $(CC) $(TARGET).o $(CHILD).o -lstdc++ -o $@
        rm -f *.o

$(TARGETA): $(TARGET).c common.h
        $(CC) $< -Bdynamic -L./ -l$(TARGET) -o $@

$(TARGETP): $(TARGET).cc common.h
        $(CXX) $< -Bdynamic -L./ -l$(TARGET) -o $@

clean:
        rm -f *.o $(LIB) $(TARGETM) $(TARGETA) $(TARGETP)
Собирается библиотека + 3 приложения:

Код: Выделить всё

[olej@dell libcpp]$ make
g++ -Wall -std=c++11 -O3 -c child.cc -o child.o
cc -Wall -std=c99 -O3 -c proxi.c -o proxi.o
cc -Wall -std=c99 -O3 proxi.o child.o -lstdc++ -o proxim 
rm -f *.o
cc -Wall -std=c99 -O3 proxi.c -Bdynamic -L./ -lproxi -o proxia
g++ -Wall -std=c++11 -O3 proxi.cc -Bdynamic -L./ -lproxi -o proxip

Код: Выделить всё

[olej@dell libcpp]$ ls *proxi*
libproxi.so  proxia  proxi.c  proxi.cc  proxim  proxip
proxim - монолитная сборка C & C++ кодов в единое приложение, без всяких библиотек (для контроля);
proxia - приложение на чистом C, использующем библиотеку DLL libproxi.so на C++, которая, в свою очередь, использует libstdc++.so;
proxip - приложение на C++, использующее ту же библиотеку libproxi.so;

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: вызов C++ кода из C

Непрочитанное сообщение Olej » 10 мар 2017, 21:13

Olej писал(а): proxim - монолитная сборка C & C++ кодов в единое приложение, без всяких библиотек (для контроля);
proxia - приложение на чистом C, использующем библиотеку DLL libproxi.so на C++, которая, в свою очередь, использует libstdc++.so;
proxip - приложение на C++, использующее ту же библиотеку libproxi.so;

Код: Выделить всё

[olej@dell libcpp]$ ./proxim
результат: TRUE
результат: FALSE
завершение процесса
завершение: подчистка перед выгрузкой
А для использования (проверки) DLL обязательно:

Код: Выделить всё

[olej@dell libcpp]$ export LD_LIBRARY_PATH=.
Или так:

Код: Выделить всё

[olej@dell libcpp]$ export LD_LIBRARY_PATH=`pwd`
А теперь используем DLL:

Код: Выделить всё

[olej@dell libcpp]$ ./proxia
результат: TRUE
результат: FALSE
завершение процесса
завершение: подчистка перед выгрузкой

[olej@dell libcpp]$ ./proxip
результат: TRUE
результат: FALSE
завершение процесса
завершение: подчистка перед выгрузкой

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: вызов C++ кода из C

Непрочитанное сообщение Olej » 10 мар 2017, 21:23

Olej писал(а):А теперь используем DLL:
Внешнее имя (для link) функций а). образуется по правилам C, б). не содержит, как в C++, в имени типов параметров и возвращаемого значенияи в). может связываться как с кодом C, так и с кодом C++:

Код: Выделить всё

[olej@dell libcpp]$ nm libproxi.so | grep ' T '
0000000000000a20 T _fini
0000000000000820 T _init
0000000000000a10 T predicat
Проверен как вызов функций из библиотеки (predicat()), так и доступ (по записи!) к полям данных (debug_level) в библиотеке.

Код: Выделить всё

[olej@dell libcpp]$ nm libproxi.so | grep ' D '
0000000000201048 D debug_level
000000000020104c D _edata
Это проделывалось (мной) для систематизации, как подготовительная часть крупного порученного мне проекта ... но, может, как сведенное в систему, окажется и ещё кому полезным?

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: вызов C++ кода из C

Непрочитанное сообщение Olej » 11 мар 2017, 00:43

Olej писал(а): Это проделывалось (мной) для систематизации, как подготовительная часть крупного порученного мне проекта
Т.е. подобная структура может применяться в самых разнообразных проектах:
- основной проект на C, построенный на API POSIX ...
- и узкий интерфейс (2, 3, 4... вызова) к обрабатывающей части...
- а сама обрабатывающая часть выполнена как DLL и написана на C++, но, самое главное, может использовать API обширных сторонних библиотек C++: STL, контейнеры, обощённые алгоритмы, компьютерное зрение OpenCV, регулярные выражения и т.д. и т.п.
- и даже там, где объектно-структурированные данные C++ (такие как контейнеры, или форматированные изображения) нужно передавать-возвращать между вызовами DLL, они в C-шной части (координирующей, вызывающей) просто преобразовываются к не типизированным указателям void* (так как это делается, например, при создании pthread_t).

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: вызов C++ кода из C

Непрочитанное сообщение Olej » 03 май 2017, 10:06

Olej писал(а): Это проделывалось (мной) для систематизации, как подготовительная часть крупного порученного мне проекта ... но, может, как сведенное в систему, окажется и ещё кому полезным?
Во-первых, обнаружил, что не прикрепил к обсуждению готовый программный код, который обсуждается... :cry:
А это не в моих правилах.
Поэтому восполняю. ;-)

А, во-вторых, для чего и кому это может быть особенно интересно?
Для фронтэнд WEB-проектов, когда основная (т.е. вызываемая) часть этого фронтэнда написана на Go, скажем (или на Python), и нужен интерфейс к какой-то развитой библиотеке, использующей C++ - та же библиотека компьютерного зрения OpenCV, как характерный пример.
Написать из Go/Python интерфейс к коду C - не проблема, а вот к специфике C++ - довольно хлопотное дело.
И вот тут на помощь может прийти такое вот связывание.
Вложения
libcpp.tgz
(2.65 КБ) 102 скачивания

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 7 гостей