質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
include

あるファイルで定義された関数や処理を、別のファイル上でも使用できるようにするプロセスをincludeと呼びます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

コールバック

コールバックは他のコードに引数として渡されるコードのことを指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

3回答

3689閲覧

C++で元ライブラリを隠蔽しつつ、自作クラスのメンバ関数をコールバック登録する機能を実現したい

zukky619

総合スコア5

include

あるファイルで定義された関数や処理を、別のファイル上でも使用できるようにするプロセスをincludeと呼びます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

コールバック

コールバックは他のコードに引数として渡されるコードのことを指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

1クリップ

投稿2021/10/23 03:29

前提・実現したいこと

c++で、イベント発生時にコールバック関数を呼び出すベンダー製ライブラリの機能を顧客に提供したいです。
しかし、すべて開示するのではなく必要な情報のみ開示するために自作ライブラリを作成して隠蔽したいです。
さらに顧客が実装する自作クラスのメンバ関数を、イベント発生時に呼び出すコールバック関数として登録できるようにしたいです。

ヘッダファイルの関係性は下記のイメージで、顧客に提供するのはmy_vendorlib_api.hppのみにしたいです。
(実際にはmy_vendorlib_listener.hppはmy_vendorlib_api.cppにてincludeすることで分離/隠蔽できるのかなと考えました)

vendorlib.hpp(ベンダー製Lib)
--> my_vendorlib_listener.hpp
--> my_vendorlib_api.hpp(顧客にはこのヘッダファイルと動的リンクライブラリのみ渡す想定)
--> sample.hpp(顧客が実装)

発生している問題・エラーメッセージ

しかしながら、自作クラスのメンバ関数をイベント発生時のコールバック関数に登録しようとすると、テンプレート記法を使うことになりヘッダーファイルにコールバック関数登録の実装を書かなければならず、my_vendorlib_listener.hppを隠蔽する方法がわからないということに悩んでいます。
そもそも思想が間違っているなども含めて、アドバイスいただけると幸いです。

my_vendorlib_api.hpp: error: invalid use of imcomplete type 'class MyVendorLibListener' this->my_vendorlib_listener->set_callback(boost::bind(fp, obj, boost::placeholders::_1)); ^

該当のソースコード

my_vendorlib_listener.hpp

c++

1#ifndef MYVENDORLIBLISTENER_H 2#define MYVENDORLIBLISTENER_H 3 4#include <functional> 5#include "vendorlib.h" 6 7class MyVendorLibListener : public vendorliblistener 8{ 9public: 10 //event関数がイベント発生時に呼ばれるので、msgの内容を絞ってcallback関数を呼ぶ実装にする 11 void event(VendorCustomMessage msg); 12 int set_callback(std::function<void (int)> callback); 13private: 14 std::function<void (int)> callback; 15}; 16 17#endif

my_vendorlib_api.hpp

c++

1#ifndef MYVENDORLIBAPI_H 2#define MYVENDORLIBAPI_H 3 4//my_vendorlib_listener.hppはソースファイル側でincludeすることで隠蔽したい 5#include <functional> 6#include <boost/bind.hpp> 7 8class MyVendorLibApi 9{ 10private: 11 class MyVendorLibListener* my_vendorlib_listener; 12public: 13 template<class T> 14 void set_callback(void(T::*fp)(int a), T *obj) 15 { 16 //テンプレートを使っているため、このヘッダファイルでmy_vendorlib_listenerを使った実装をせざるを得なくなりエラーとなる 17 this->my_vendorlib_listener->set_callback(boost::bind(fp, obj, boost::placeholders::_1)); 18 } 19}; 20 21#endif

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

matukeso

2021/10/23 03:51

Apiクラス側のset_callbackでもstd::function使えばいいとおもうけど、何か理由があるの?
zukky619

2021/10/23 04:31

ありがとうございます。知識不足につきイメージがついていない部分もあります。 自作関数のメンバを登録する際、sample.cppの自作クラスのインスタンスthisを渡さなければならないという理解で、 template<class T> void set_callback(void(T::*fp)(int a), T *obj) とするのかなと思ったのですが、std::functionを使うとその点が解消されるのでしょうか。予めbindしたものを受け取るイメージでしょうか?
guest

回答3

0

1.関数ポインタとvoid*ペア。Windows APIとかで頻出のパターン。DLL境界(言語、バージョンの境界)超えてても大丈夫。使う方はサンク関数を渡すパターン。使用例はこんな感じ。

C++

1api->set_callback( OnCallbackThunk, this ); 23void OnCallbackThunk( void *data ){ 4 static_cast<UserClass*>( data )->OnCallback(); 5}

2.std::funtion。使う方がthisとかをキャプチャしたラムダを渡す。使用例は下記みたいな感じ。ただしライブラリと使用側でコンパイル環境を揃える必要がある。

c++

1api->set_callback([this](){ oncallback();});

投稿2021/10/23 05:59

matukeso

総合スコア1590

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

いったんtemplateの型の解決をMyVendorLibApi内部で行って、MyVendorLibListenerへcallbackを送るのは別関数にすれば隠蔽できるかと思います。

C++

1// MyVendorLibApi.hpp 2class MyVendorLibApi 3{ 4private: 5 class MyVendorLibListener* my_vendorlib_listener; 6 std::function<void(int)> callback; 7 8public: 9 template<class T> 10 void set_callback(void(T::*fp)(int a), T *obj) 11 { 12 this->callback = boost::bind(fp, obj, boost::placeholders::_1); 13    push_callback(); 14 } 15private: 16 void push_callback( void ); 17}; 18 19//MyVendorLibApi.cpp 20#include "my_vendorlib_listener.hpp" 21 22void MyVendorLibApi::push_callback( void ){ 23 24 my_vendorlib_listener->set_callback( this->callback ); 25}

修正

C++

1// MyVendorLibApi.hpp 2class MyVendorLibApi 3{ 4private: 5 class MyVendorLibListener* my_vendorlib_listener; 6 7public: 8 template<class T> 9 void set_callback(void(T::*fp)(int a), T *obj) 10 { 11 set_callback( boost::bind(fp, obj, boost::placeholders::_1) ); 12 } 13 14 void set_callback( std::function<void(int)> callback ); 15}; 16 17//MyVendorLibApi.cpp 18#include "my_vendorlib_listener.hpp" 19 20void MyVendorLibApi::set_callback( std::function<void(int)> callback ){ 21 22 my_vendorlib_listener->set_callback( callback ); 23}

投稿2021/10/23 05:55

編集2021/10/23 06:26
Serbonis

総合スコア581

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Serbonis

2021/10/23 06:07

push_callback( std::function<void(int)> callback )にすれば、callbackをメンバ変数で持つ必要もないかも知れないです。
Serbonis

2021/10/23 06:18

private: void push_callback( std::function<void(int)> callback ); ↓ public: void set_callback( std::function<void(int)> callback ); set_callbackのオーバーロードにすれば、汎用性が高くなるかと思います。
zukky619

2021/10/27 09:05

ありがとうございます。こちらの方法で解決できました。
guest

0

話の途中でいきなりテンプレートが登場した意味がわかりません.

自作クラスのメンバ関数をイベント発生時のコールバック関数に登録

することに何らかの工夫を要するのだとしても,その作業を行うべきは,「あなたの自作ライブラリを使う側(自作クラスとかいうのを実装している側)」なのではないでしょうか.

投稿2021/10/23 03:59

fana

総合スコア11658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/10/23 04:04

あなたのライブラリはコールバックとして設定できる物の型を,ライブラリを使う側に強制すればいいだけです.それがライブラリの仕様です. (当然,なるべく使いやすい形にしたほうがよいですが) で,使う側はそれに合わせて使います. 使うものの仕様に合わせるための作業は使う側が行うことです.
zukky619

2021/10/23 04:37

ありがとうございます。知識不足につきイメージがついていない部分もあります。 テンプレートを用いた理由は、sample.cppの自作クラスのインスタンスthisを渡さなければならないという理解で、 template<class T> void set_callback(void(T::*fp)(int a), T *obj) とするのかなと思ったからです。 おっしゃるとおり、例えばクラスのメンバ関数ではなく、通常の関数のみ受け付ける仕様にしてしまえば本件で悩まなくて良いなと思うのですが、なるべく使いやすい形にしたく考えておりました。
dodox86

2021/10/23 06:22

DLLにして関数をEXPORTするのであれば、C++であるとメンバー関数の名前のマングリングの問題が出てきますよ。
fana

2021/10/24 01:04

std::function と 自作クラスのメンバ関数 の間を取り持つための template を用いた実装を, ライブラリ側で提供したいのだとしても, それを MyVendorLibApi のメソッドとしてしまうと my_vendorlib_listener.hpp を隠蔽できなくなる であれば MyVendorLibApi が要求する型は (MyVendorLibListener と同じ)std::function にして, そこにtemplateで突っ込む作業を行うだけのヘルパを別途提供する形とかでもよいのでは?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問