0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CとC++の合成(extern "C"でOpenCV可視化)

0
Posted at

はじめに

特定の条件において、実装側をC言語で、可視化をC++でのようにコンポーネント分割したい状況があったので、その場合に活用できる実装方法をまとめます。
パフォーマンス重視・移植性重視のアルゴリズム実装では、 C言語を選びたい場面は今でも多くあります。一方で、可視化・デバッグ・UI まで含めると OpenCV をはじめとした C++ライブラリの表現力 は非常に魅力的です。

ここでは、

  • 計算ロジックはC
  • 可視化はC++(OpenCV)
  • 両者を extern "C" で接続
    という構成で、可視化付きシミュレーションを構築する方法を解説します。

この構成は単なる言語混在テクニックではなく、

  • C資産を保ったままの可視化
  • アルゴリズムとUIの責務分離
    といった点でも実用性の高いパターンだと考えています。

目的

  • Cで実装した粒子シミュレーションを動かす
  • 各フレームの状態を C++ 側に渡す
  • OpenCV ウィンドウでリアルタイム描画する

extern "C"とは

cとc++の異なる点

C++では、関数名に引数型などを含めた名前修飾が行われます。

void draw(int);
void draw(double);

これが可能なのは、コンパイラが内部的に異なる名前を生成しているからです。

一方、C言語には名前修飾がありません。
そのため C++関数をそのままCから呼ぶことはできません。

そこで使うのが extern "C" です。

extern "C" {
  void foo(int);
}

この指定により、名前修飾を行わず、C互換のリンケージになることで、結果として Cからリンク可能な関数 になります。


実装ステップ

1. プロジェクト構成例

.
├── main.c           # C: シミュレーションロジック
├── particle.h       # C/C++共有データ構造
├── visualizer.h     # C向けインターフェース
├── visualizer.cpp   # C++: OpenCV描画
└── Makefile

2. 共有データ構造(particle.h)

C/C++の両方から使うため、純C構文のみで定義します。

#ifndef PARTICLE_H
#define PARTICLE_H

typedef struct {
    double x;
    double y;
} Particle;

#endif

3. C++側の描画クラス(visualizer.cpp)

OpenCVを用いた描画処理は、C++のクラスに閉じ込めます。

#include <opencv2/opencv.hpp>
#include "particle.h"

class Visualizer {

std::string window;

cv::Mat image;

  

public:

Visualizer(const char* name, int w, int h)

: window(name), image(h, w, CV_8UC3) {

cv::namedWindow(window);

}

  

void update(Particle* p, int n) {

image.setTo(cv::Scalar(0, 0, 0));

for (int i = 0; i < n; ++i)

cv::circle(image, cv::Point(int(p[i].x), int(p[i].y)), 4, cv::Scalar(0, 255, 0), -1);

cv::imshow(window, image);

}

  

~Visualizer() {

cv::destroyWindow(window);

}

};

この時点では Cからは一切見えません。

4. C向けブリッジAPI(visualizer.h)

C側には 不透明ポインタ(Opaque Pointer) だけを公開します。

#ifndef VISUALIZER_H
#define VISUALIZER_H

#include "particle.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef void* VisualizerHandle;

VisualizerHandle visualizer_init(const char* name, int w, int h);
int visualizer_update(VisualizerHandle, Particle*, int);
void visualizer_close(VisualizerHandle);

#ifdef __cplusplus
}
#endif

#endif

Step 5: ブリッジ関数の実装(visualizer.cpp)

#include "visualizer.h"

extern "C" {

VisualizerHandle visualizer_init(const char* name, int w, int h) {
  return new Visualizer(name, w, h);
}

int visualizer_update(VisualizerHandle h, Particle* p, int n) {
  auto* v = static_cast<Visualizer*>(h);
  v->update(p, n);
  return cv::waitKey(10) == 'q' ? -1 : 0;
}

void visualizer_close(VisualizerHandle h) {
  delete static_cast<Visualizer*>(h);
}

}

Step 6: C側メインロジック(main.c)

#include <stdlib.h>
#include "visualizer.h"

#define N 100
#define W 640
#define H 480

int main() {
  Particle p[N];
  for (int i = 0; i < N; i++) {
    p[i].x = rand() % W;
    p[i].y = rand() % H;
  }

  VisualizerHandle viz = visualizer_init("Simulation", W, H);

  while (1) {
    for (int i = 0; i < N; i++) {
      p[i].x += (rand() % 3 - 1) * 2;
      p[i].y += (rand() % 3 - 1) * 2;
    }
    if (visualizer_update(viz, p, N)) break;
  }

  visualizer_close(viz);
}

C側からは OpenCVの存在すら見えません。

7. Makefile

CC=gcc
CXX=g++
TARGET=particle_sim

C_SRCS=main.c
CPP_SRCS=visualizer.cpp
OBJS=$(C_SRCS:.c=.o) $(CPP_SRCS:.cpp=.o)

CFLAGS=-std=c11 -Wall -I.
CXXFLAGS=-std=c++11 -Wall -I. $(shell pkg-config --cflags opencv4)
LDFLAGS=$(shell pkg-config --libs opencv4)

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CXX) $^ -o $@ $(LDFLAGS)

%.o: %.c
	$(CC) $(CFLAGS) -c $<

%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $<

clean:
	rm -f $(OBJS) $(TARGET)

まとめ

この構成のポイントは以下です。

  • 計算コアはCで完結

  • 可視化はC++に隔離

  • extern "C" による安全な接続

  • 不透明ポインタによる実装隠蔽

この設計は、「CとC++を混ぜる」のではなく、役割を分けて接続するという発想で使ってえるかと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?