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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C++

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

Q&A

解決済

4回答

10091閲覧

C++で、クラス定義を隠す方法はないでしょうか?

Chironian

総合スコア23272

C++

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

4グッド

3クリップ

投稿2015/12/15 03:36

編集2015/12/15 07:58

C++ライブラリを開発してます。ほとんどのクラスがテンプレートです。

内部的なクラス・テンプレートが多数あるのですが、混乱を招くだけなのでこれらをユーザに使わせたくないのです。普通にドキュメントで説明しないだけでも一定の効果はあると思いますが、可能であれば「使えない」、もしくは、「使いにくく」したいと考えてます。

必須要件ではないし、C++言語仕様上無理っぽい気がするので諦める方向なのですが、もし、何か使えそうなテクニックをご存知の方がいれば、ぜひ教えて下さい。


【追記】
privateな名前空間だよなと思いつつ、流石にあり得ないので検索することに思い至って無かったのですが、検索してみたらいくつか当たりました!
http://tiri-tomato.hatenadiary.jp/entry/20130209/1360357831
http://oshiete.goo.ne.jp/qa/6902744.html

catsforepawさんの回答も含めて今のところ下記方法が考えられそうです。

内部用の名前空間をわけ、長い名前をつけてて使い難くする。

無名名前空間を途中にかまして、使えなくする。
ただし、上書きするための名前空間定義の場所が気になる。最後にしないとダメかも? 後ほど確認予定。

全部privateなclass内に定義し、friend指定する。
異なる場所で内部クラスを定義する度にclassを別に定義する必要があるし、クラス内クラス・テンプレートは結構面倒なので、手間がかかりそう。

ヘッダの最後で内部名前空間名を#defineする。
解りやすくてよいけど、インクルード群の「最後」を決めることができないので使えない。


【②を検証】
http://tiri-tomato.hatenadiary.jp/entry/20130209/1360357831
に記載されている方法を確認しました。びっくりですが使えそうな感じがします。

以下、上記ページから引用です。

C++

1namespace myLib { 2 namespace { 3 // privateな実装 4 namespace prv_impl { 5 void hoge_impl() { /* ほにゃらら */ } 6 } 7 // publicな定義 8 void hoge() { prv_impl::hoge_impl(); } 9 } 10 namespace prv_impl {}; // 実装が書いてある方のprv_implへのアクセス殺し。 11}

C++

1void func() { 2 myLib::hoge(); // これはOK。 3 // でも myLib::prv_impl::hoge_impl(); はNG! 4}

(引用終わり)

namespace prv_impl {}; // 実装が書いてある方のprv_implへのアクセス殺し。

を書く場所が気になったのですが、namespace myLibの先頭へ移動しても意図通りでした。
また、msvc2015, MinGW 4.9.2, MinGW 5.2.0全てで、

  1. myLib内の無名namespaceから、hoge_impl()をアクセス可
  2. myLib外部からは、hoge_impl()はundefined symbol
  3. myLib直下からは、hoge_impl()するとprv_implが曖昧

となりました。

文法的にも問題ないように感じますので、使えそうな気がします。

C++

1namespace myLib 2{ 3 namespace prv_impl {}; 4 namespace { namespace prv_impl 5 { 6 // privateな定義 7 }} // (←この2重かっこが見難くて悔しい) 8 9 namespace 10 { 11 // publicな定義 12 } 13}

一瞬外部公開クラスを使えないようにも見える点に少し問題ありますが、使っても良さそうな気がします。

皆さんはどう思いますか?(それと、これは別質問にするべきですか?)

tanat, catsforepaw, ps13zier, suittizihou👍を押しています

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

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

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

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

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

Stripe

2015/12/15 11:19

そのライブラリはどのように配布しますか? ソースコード?コンパイル済のバイナリ?
Chironian

2015/12/15 12:31

テンプレートが多くバイナリ配布の効果が薄いので、ソース配布の方向で検討中です。
guest

回答4

0

ヘッダーに実装を置く以上、「使えない」ことにはできないでしょうから、私の場合は内部使用のクラスは異なるnamespaceに置いています。

C++

1namespace MyClassLibraryInternal_ 2{ 3 // 内部使用クラスの宣言 4} 5 6namespace MyClassLibrary 7{ 8 // 公開クラスの宣言 9} 10namespace MCL = MyClassLibrary;

とりあえず、こんな具合にして内部使用のクラスはそれと判るようにして「使いにくく」しています。

投稿2015/12/15 04:32

編集2015/12/15 04:55
catsforepaw

総合スコア5938

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

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

Chironian

2015/12/15 05:26

回答、ありがとうございます。 なるほど!! 内部用は「長い」名前にして、外部用は短い名前も提供することでメリハリを付けるのですね。参考にさせて頂きます。 流石にあり得ないと思って検索してなかったのですが、catsforepawさんの回答をみてやはり需要がある筈と思い、privateなnamespaceを検索したら、同じようなこと考えている人いました。ちょっとびっくり。質問文の方に追記します。
catsforepaw

2015/12/15 07:16

②の検証ありがとうございます。確かに隠せてますね。なかなか興味深いです。 これまで「使いにくく」する対策だけで特に問題がなかったのでそれ以上のことは試してみませんでしたが、本気で隠したいときはこのテクニック使えますね。複数ヘッダーに分けるのは難しそうですが。 ところで、下のC++のコードですが、「アクセス殺し」が入っていないのでそのままではprv_implにアクセスできてしまいます。それと、prv_implは外部公開クラスの前に記述しないと、外部公開クラスから参照しようとするとエラーになります(記入ミス?)。
Chironian

2015/12/15 07:54

> 「アクセス殺し」が入っていないのでそのままではprv_implにアクセスできてしまいます。 おっと、そうでした。myLib→無名namespaceをセットにすることに気を取られてました。順序もそうですね。 namespaceは分割できるので、複数ヘッダに分割してもあまり問題はでないような気がします。
catsforepaw

2015/12/15 08:51

試してみましたが、おっしゃる通り非公開部分を別ヘッダーにして、複数のヘッダーからインクルードして使うということもできました。
guest

0

catsforepawさんと同意見ですが、個人的にはdetail名前空間を使います。これはBoost Libraryと同じ命名ルールですし、比較的に広く普及している規約かと思われます。

無名名前空間を使った"賢い"方法も悪くは無いと思いますが、ユーザ・プログラマからは何も隠れていないと考えます。もちろんコンパイルエラーを引き起こせば目的は達するでしょうけど、直接的な原因を示唆するエラーメッセージの出力はあまり望めませんし、そうなると明文化された規約(ユーザはdetail名前空間以下を使うな)と大差ありません。

投稿2015/12/15 15:04

yohhoy

総合スコア6189

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

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

Chironian

2015/12/15 16:04

回答、ありがとうございます。 なるほど、boostのdetailは「使うな」の意味が込められているのですね。少しそんな雰囲気を感じてました。 英語が不得意なせいと思いますが明文化された規約を見つけられません。boostのサイトで 検索してもそれっぽい結果が出てこないです。もし、その規約が書かれているページをご存知でしたら教えていただけないでしょうか? 実は、ipcdetail内の魅力的な機能を使ってます。もし、ipcdetailも使うなだったら、代替案を考えないと。 しかし、detailは「詳細」だし短いから使うなと言う意思を示すにはちょっと弱いですね。こちらの方法で対応する場合は、internal_space等のもう少し長くて「内部」を強調した名前を私なら選びそうです。
guest

0

自己解決

結局、自力で見つけた無名名前空間方式で進めようと思いますので、自己解決とさせて頂きます。
考え方は質問に追記した通りです。最終的にはマクロ使って下記のようにする予定です。

C++

1#define EXTERNAL namespace internal {}; namespace 2#define INTERNAL namespace internal 3 4namespace myLib { EXTERNAL 5{ 6 INTERNAL 7 { 8 void hoge_impl() { /* ほにゃらら */ } 9 } 10 11 void hoge() { internal::hoge_impl(); } 12}}

catsforepawさん、yohhoyさん、yubaさん、回答ありがとうございました。


【2015/12/26追記】
その後、開発中のプログラムへ適用してみたところ、制約が多く使用を断念しました。
Qiitaのアドベント・カレンダーで報告してしまったので、詳しくはそちらで説明しています。

無名空間は外部リンケージがない

実装を全部ヘッダに置く必要がある・・・コンパイル時間とオブジェクトが無駄に増大

テンプレートのみINTERNAL化

I/F用のテンプレートをEXTERNALに置いて中継が必要・・・どうしても一部は隠せない

それを外から基底クラスやメンバで使うとgccが警告を出す・・・6.0で出さなくするオプションが付くらしい

投稿2015/12/16 05:32

編集2015/12/26 13:03
Chironian

総合スコア23272

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

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

0

テンプレートクラスの場合、実装を別のヘッダファイルに書いてインクルードする、という方法をBoostで採用しているそうです。
http://qiita.com/MasayaMizuhara/items/37f8c2a5462a4f7f8ea0

投稿2015/12/15 03:49

yuba

総合スコア5568

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

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

Chironian

2015/12/15 04:17 編集

回答ありがとうございます。 しかし、残念ながらこれは通常はcppに書くコードを別ファイルに記述することが目的のようです。ユーザは普通に当該関数を呼び出せますので、使い難くもなっていないように思います。 「使い難く」は呼び出すことに心理的な抵抗感を与えることができる程度でも良いと思っているのですが... なお、できればクラス全部を使い難くできる方法が好ましいのですが、メンバ関数を使い難くできるだけでもそれなりに効果的と思います。 【追記】 ああ、すいません。pImplイデオムへの言及が誤解を招いたかも知れません。 この下りを削除します。
yuba

2015/12/15 07:59

質問意図を誤解していたようです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問