none
関数ポインタをマネージドクラスでラップする方法 @ C++/CLI RRS feed

  • 質問

  • お世話になります。

    OpenGL では、glext.h や wglext.h というヘッダの中で様々な関数が定義されているのですが、それらを使うには wglGetProcAddress という関数で DLL 内 から 関数ポインタ を取ってくる必要があります。

    例えば以下のクラスでは、Create という初期化関数を実行することで、wglChoosePixelFormatARB という関数のポインタを取得し、"PFN関数名PROC"ポインタ型 の メンバ変数へ確保しています。

    #pragma once
    #pragma comment( lib, "opengl32.lib" ) 
    #include <windows.h>  
    #include "gl.h"        
    #include "wglext.h"
    namespace WGLextTools
    {                                    
        using namespace System;     
        public ref class TWGLEXT
        {                         
        private:
            static PFNWGLCHOOSEPIXELFORMATARBPROC _wglChoosePixelFormatARB; 
        public:
            static void Create();
            static property IntPtr wglChoosePixelFormatARB { IntPtr get(){ return (IntPtr)_wglChoosePixelFormatARB; } } 
        };
        void TWGLEXT:: Create()
        {
            _wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress( "wglChoosePixelFormatARB" ); 
        }
    }
    
    

    しかしこの DLL を他のプロジェクトから利用しようとすると、関数ポインタは IntPtr型 でしか渡せないので、使うたびにいちいちキャストする必要に迫られます。

    BOOL ((PFNWGLCHOOSEPIXELFORMATARBPROC)TWGLEXT::wglChoosePixelFormatARB.ToPointer())( HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats )
    
    

    もっとも、関数ポインタを直接渡すというのは芸がないので、この方法だとこれが限界かと思いますが、もっと何か別の高度なテクニックを使って、美しくラップする方法があるのでしょうか?

    もちろん、一つ一つメソッドでくるんでいく方法もありますが、取り込みたい関数は何百とありますし、ポインタ型の引数まで適宜 IntPtr に置換していくとなると、気が遠くなってしまいます。

    static bool TWGLEXT:: wglChoosePixelFormatARB(~)
    {
        return _wglChoosePixelFormatARB(~);
    }
    
    

    何か策がありましたら、何卒ご教示下さい。


    • 編集済み luxidea 2011年9月17日 18:41
    2011年9月17日 18:38

回答

  • 使いたい関数を一つずつラップするのが妥当でしょう。
    もしくは、ある程度固まった処理でラップするかでしょうか。
    (1 関数ごとじゃなく、○○する処理というくくりで、アルゴリズムも含めるとか)

    結局、別のマネージ層で ToPointer() やら、キャストやら、めんどくさい処理があるわけですよね?
    そうであれば、OpenGL の関数を直接使う部分は C++/CLI DLL の 1 つに絞り、そこでマネージ型の変数からネイティブ型の変数にキャストするなど、泥臭い処理をラップするまでやるのが、ラッパーとしてあるべき姿かなと思います。

    ところで、マネージクラス(ref class)として作らないといけない理由はあるのでしょうか?
    マネージクラスは C# とか、ほかのマネージ層に見せるときに利用するぐらいにとどめて、ネイティブコードで書いた方がよい部分は、ネイティブクラスにするということでいいんですが。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年9月17日 23:06
    モデレータ

すべての返信

  • 使いたい関数を一つずつラップするのが妥当でしょう。
    もしくは、ある程度固まった処理でラップするかでしょうか。
    (1 関数ごとじゃなく、○○する処理というくくりで、アルゴリズムも含めるとか)

    結局、別のマネージ層で ToPointer() やら、キャストやら、めんどくさい処理があるわけですよね?
    そうであれば、OpenGL の関数を直接使う部分は C++/CLI DLL の 1 つに絞り、そこでマネージ型の変数からネイティブ型の変数にキャストするなど、泥臭い処理をラップするまでやるのが、ラッパーとしてあるべき姿かなと思います。

    ところで、マネージクラス(ref class)として作らないといけない理由はあるのでしょうか?
    マネージクラスは C# とか、ほかのマネージ層に見せるときに利用するぐらいにとどめて、ネイティブコードで書いた方がよい部分は、ネイティブクラスにするということでいいんですが。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年9月17日 23:06
    モデレータ
  • 毎度お世話になります。

    使いたい関数を一つずつラップするのが妥当でしょう。
    やはりそういうことですよね。了解しました。
    OpenGL の関数を直接使う部分は C++/CLI DLL の 1 つに絞り、そこでマネージ型の変数からネイティブ型の変数にキャストするなど、泥臭い処理をラップするまでやるのが、ラッパーとしてあるべき姿かなと思います。
    はい、そういうことですよね。全てを覆い隠して、外からはなるべくシンプルに使って貰うと。 思想が分かってきました。
    ネイティブコードで書いた方がよい部分は、ネイティブクラスにするということでいいんですが。
    将来的に OpemGL フレームワークとして C# からなどの利用も考えているので、なるべくマネージドで作ろうと挑戦しています。 近年のマネージド言語の台頭も、漠然とプログラムが簡単になるのかなと思っていましたが、色々とマネージドのカルチャーに慣れないと難しいものでね。 まだまだ切磋琢磨していきます。

    勉強になりました。この度もありがとうございます。

    2011年9月18日 3:15
  • ところで wglGetProcAddress() がエラーを返すことはないのでしょうか? 各関数について得られたアドレスが正常かどうかなどチェックが必要で、そのチェックをC#で行うのもおかしな話で、C++/CLIに含める必要があるのでは? もしくはそれをそのままdllexportするならネイティブのC++としたりもできるかも。
    2011年9月19日 4:10
  • はい確かに、関数をサポートしていないグラフィックボードもあるので、まさにその通りです。その辺もマネージドなライブラリとして上手くラップできればと、鋭意実装中です。

    2011年9月19日 11:02