94767


##### 質問内容

タイトル通りなのですがフレームワークの制作で利用で使える(参照出来る)物と使えない(参照出来ないように)ようにする物とを分ける方法が知りたいが知りたいです現状部のように実装しましたが本当はどうやって実装するのが一般的なのか知りたいです。

##### 現状

1,フレームワークのヘッダーファイルをフレームワーク内のヘッダーで読み込んでそのヘッダーが利用側で使われることを利用してフレームワークの外と中で使えるのようにしています。

2,利用側では使えない(参照出来ないように)にする方法は思いつきません。例としてコンテキストクラスですが利用側では関数を使って初期化して利用側では使えない(参照出来ないように)にしたいです

3,コンポーネント指向です

##### 知りたいこと

フレームワークの制作で`利用側が使える(参照出来る)ようにしてフレームワーク側でも使えるようにする物`と`利用側で使えない(参照出来ないように)にする物`はどうやってコードを書いたらいいのか知りたい現状部の1,2,をどうやって実装するのが一般的なのか知りたい

##### コンテキスト

[code]

    #ifndef ___WINDOWCONTEXT_HPP___
    #define ___WINDOWCONTEXT_HPP___
    
    #include <iostream>
    #include <array>
    #include <vector>
    #include <memory>
    
    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
    #include <glm/glm.hpp>
    
    
    namespace FrameWork
    {
    
        class WindowContext
        {
    
        public:
    
            WindowContext(const glm::ivec2 s,const char* t);    //コンストラクタ
            ~WindowContext();                                   //デストラクタ
            
            static void Init();
    
    
            operator bool();                    //bool 演算子
            void SwapBuffers()const;            //ダブルバッファリング
            const glm::ivec2 getSize() const;   //サイズ
            void setCurrentContext();           //クライアントコンテキスト
    
            //イベント処理
            static void Resize_Event(GLFWwindow* win, const int width, const int height);                                       //画面サイズ
            static void DragAndDrop_Event(GLFWwindow* win, const int num, const char* str[]);                                   //ドラック＆ドロップ
            static void MouseScroll_Event(GLFWwindow* win, const double x, const double y);                                     //マウススクロール
            static void KeyInputChar_Event(GLFWwindow* win, const unsigned int n);                                              //テキスト入力
            static void KeyInput_Event(GLFWwindow* win, const int key, const int scanCode, const int action, const int mods);   //キー入力
            static void MouseMove_Event(GLFWwindow* win, const double xpos, const double ypos);                                 //マウス
    
    
            bool getKeyInput(int keyCode);
            float getDeltaTime();
            static WindowContext *context;
    
        private:
    
            float deltaTime;
            float time;
            std::string title;  //ウインドウタイトル
            glm::ivec2 size;    //画面サイズ
    
            GLFWwindow* window; //ウインドウコンテキスト
    
            std::array<short int,GLFW_KEY_MENU> keyBoard;   //キー入力
            unsigned int inputKey;                          //テキスト入力
    
            double mouseWheel;              //マウスホイール
            glm::dvec2 mousePosition;       //マウス座標
            std::vector<std::string> drop;  //ドラック＆ドロップ
        };
    
    
    }
    
    
    #endif
    
[/code]


CC BY-SA 4.0
2023-05-06T01:59:26.520
94763
2023-05-12T07:32:06.277
2023-05-06T03:06:36.080
55177
55177
question
c++
フレームワークの制作で利用側に公開する物と非公開にする物とを分ける方法が知りたい


質問内容が今一つはっきりしませんが、APIのインターフェイスと実装を分離して実装を隠蔽したいという話と解釈しました。  
一般的には、Pimpl(pointer to implementation)イディオムを使います。  
Pimplは、ユーザーに公開するヘッダーファイル内のクラス定義では前方宣言した不完全な型へのポインターのみを保持するようにし、その完全な型の定義はユーザー非公開のファイルで行うことで、プライベートメンバー（実装）を隠蔽します。  
以下、実装例です。

前提：

  * 公開クラス：Window
  * 非公開クラス：WindowContext
  * Windowクラスは、メンバーとしてWindowContextクラスのオブジェクトを持ちたい

include/external/FrameWork/Window.hpp:

[code]

    #ifndef FRAMEWORK_WINDOW_HPP
    #define FRAMEWORK_WINDOW_HPP
    
    #include <memory>
    
    namespace FrameWork {
    
    class Window
    {
      private:
        class Impl;
        std::unique_ptr<Impl> m_impl;
    
      public:
        explicit Window(int i);
        ~Window();
        void set(int i);
        int get() const;
    };
    
    }
    
    #endif
    
[/code]

include/internal/FrameWork/WindowContext.hpp:

[code]

    #ifndef FRAMEWORK_WINDOWCONTEXT_HPP
    #define FRAMEWORK_WINDOWCONTEXT_HPP
    
    namespace FrameWork {
    
    class WindowContext
    {
        // ...
    };
    
    }
    
    #endif
    
[/code]

Window.cpp:

[code]

    #include "FrameWork/Window.hpp"
    #include "FrameWork/WindowContext.hpp"
    
    namespace FrameWork {
    
    class Window::Impl
    {
      private:
        WindowContext *m_windowContext;
        int m_i;
    
      public:
        Impl(int i)
          : m_windowContext{new WindowContext}
          , m_i{i}
        {
        }
      
        void set(int i)
        {
            m_i = i;
        }
    
        int get() const
        {
            return m_i;
        }
    };
    
    Window::Window(int i)
      : m_impl{std::make_unique<Window::Impl>(i)}
    {
    }
    
    Window::~Window() = default;
    
    void Window::set(int i)
    {
        m_impl->set(i);
    }
    
    int Window::get() const
    {
        return m_impl->get();
    }
    
    }
    
[/code]

main.cpp:

[code]

    #include "FrameWork/Window.hpp"
    
    int main()
    {
        FrameWork::Window window(1);
        int i = window.get();
        window.set(i);
    }
    
[/code]

* * *

以下、Pimplイディオムのメリット・デメリット。  
参考：

  * [C++のためのAPIデザイン](https://www.sbcr.jp/product/4797369151/)

## メリット

  * 情報の隠蔽  
プライベートメンバー（実装）を完全に隠蔽できる。  
マクロ（`#define private public`）を使ってプライベートメンバーにアクセスする裏技も使えない。

  * インターフェイスと実装の結合度の削減
  * コンパイル時間の高速化
  * バイナリー互換性の向上
  * レイジー割り当て

## デメリット

  * オーバーヘッドの増加  
クラスのオブジェクト毎に追加の実装オブジェクトを保持し、new・deleteしなければならない。  
プライベートメンバーへのアクセスが、実装オブジェクト経由になる。

  * 実装、デバッグの手間の増加
  * コンパイラーがconstメンバー関数内でのメンバー変数の変更を検知できない

* * *

[マルチポスト先](https://teratail.com/questions/qgect1m5qyih06)のようにWindowContextクラスのオブジェクトを1つに限定したい場合は、一般的には[Singletonパターン](https://ja.wikipedia.org/wiki/Singleton_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)を使います。


CC BY-SA 4.0
2023-05-06T07:35:45.093
94767
2023-05-09T13:18:46.553
2023-05-09T13:18:46.553
7291
7291
94763
answer


正直，本当は何がしたいのかが未だにわかりませんが…

## 1.「ウィンドウコンテキスト」という何かが存在すること自体を利用側に知らせる必要が無いならば

単純に `WindowContext` 型そのものを利用側に対して公開しなければよいだけです．  
あなたのフレームワークの内側で勝手に適切に扱っていれば良いだけの話です．

## 2\. 「ウィンドウコンテキスト」という何かが存在することを利用側が知る必要がある場合

この場合でも， `WindowContext` なる具体的な型を利用側に見せずとも…

### 2-1. 利用側から「初期化せよ」等の指示ができれば十分な場合

「ウィンドウコンテキストを初期化する」といったような関数を用意して利用側に使わせれば十分でしょう．  
（「このライブラリを使う際には最初に一度だけこの初期化関数を呼んでね」みたいなの，たまに見かけますよね．）

### 2-2. 利用側がコンテキストのインスタンスあるいはそれ相当の物を握る必要がある場合

  * [別の場所](https://teratail.com/questions/qgect1m5qyih06)で回答しましたが，具体型ではなくインタフェースクラスを公開する手があります．
  * Win32API の「デバイスコンテキスト」のようにしてしまう手もあるかもしれません．  
（利用側は何らかのハンドルしか扱わないけど，そのハンドルを通してインスタンスを管理したりフレームワーク側に対して「これを使え」と指定したりする，という形．）


CC BY-SA 4.0
2023-05-12T07:32:06.277
94829
2023-05-12T07:32:06.277
52236
94763
answer