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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

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

Q&A

解決済

4回答

2724閲覧

C#で値=>型のmappingみたいなことをコンパイル時にやることは可能か?

yumetodo

総合スコア5850

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

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

1グッド

1クリップ

投稿2017/01/09 05:49

編集2017/01/09 12:24

C++の場合

cpp

1struct SHARE_INFO_0 { 2 LPWSTR shi0_netname; 3}; 4struct SHARE_INFO_1 { 5 LPWSTR shi1_netname; 6 DWORD shi1_type; 7 LPWSTR shi1_remark; 8}; 9struct SHARE_INFO_2 { 10 LPWSTR shi1_netname; 11 DWORD shi1_type; 12 LPWSTR shi1_remark; 13 DWORD shi2_permissions; 14 DWORD shi2_max_uses; 15 DWORD shi2_current_uses; 16 LPWSTR shi2_path; 17 LPWSTR shi2_passwd; 18};

のような構造体群と値を

cpp

1template<typename ...Args> 2struct first_enabled {}; 3 4template<typename T, typename ...Args> 5struct first_enabled<std::enable_if<true, T>, Args...> { using type = T; }; 6template<typename T, typename ...Args> 7struct first_enabled<std::enable_if<false, T>, Args...>: first_enabled<Args...> {}; 8template<typename T, typename ...Args> 9struct first_enabled<T, Args...> { using type = T; }; 10 11template<typename ...Args> 12using first_enabled_t = typename first_enabled<Args...>::type; 13 14template<int InfoLevel> 15using SHARE_INFO = first_enabled_t< 16 std::enable_if<0 == InfoLevel, SHARE_INFO_0>, 17 std::enable_if<1 == InfoLevel, SHARE_INFO_1>, 18 std::enable_if<2 == InfoLevel, SHARE_INFO_2> 19>;

のようにmappingして

cpp

1template<int infoLevel> 2SHARE_INFO<infoLevel> f(){ return {};}

のようにコンパイル時に値=>型に変換できますが、

http://melpon.org/wandbox/permlink/qSNWfttreE9limQg

C#の場合こういうことはできないのでしょうか?


追記

状況としてはWin32APIをWrapしようとしています。
Win32APIのNetShareEnum関数は第二引数によって、第三引数から返されるものが変化し、
第二引数が0, 1, 2, 502の時、それぞれSHARE_INFO_0,SHARE_INFO_1,SHARE_INFO_2,SHARE_INFO_502の4つの構造体のいずれかを指すポインタを返します。

とりあえず第二引数に2を指定するものをラップしたのが

https://bitbucket.org/yumetodo/netshareenum/src/d2f19f705267259cf3b899cf0bbcbacd9fe71e05/NetShareEnum/Program.cs?at=master&fileviewer=file-view-default

です。

csharp

1public static List<SHARE_INFO_2> NetShareEnum_2_wrap(string serverName) { 2 var re = new List <SHARE_INFO_2> { }; 3 Int32 er = 0; 4 Int32 tr = 0; 5 Int32 resume = 0; 6 int res; 7 do 8 { 9 res = NetShareEnum(serverName, 2, out var bufPtr, -1, ref er, ref tr, ref resume); 10 if (res != (int)NET_API_STATUS.NERR_Success && res != (int)NET_API_STATUS.ERROR_MORE_DATA) break; 11 var infoP = bufPtr; 12 for (int i = 0; i < er; ++i) 13 { 14 re.Add(Marshal.PtrToStructure<SHARE_INFO_2>(infoP)); 15 infoP = IntPtr.Add(infoP, Marshal.SizeOf<SHARE_INFO_2>()); 16 } 17 NetApiBufferFree(bufPtr); 18 } while (res == (int)NET_API_STATUS.ERROR_MORE_DATA); 19 return re; 20}

これを一般化するために

まずはdynamicを使うことを考えましたが、できれば静的にやりたいなと思い却下し

空のinterfaceクラスをでっち上げてSHARE_INFO_xクラスに指定し、Genericsの制約に使用することも考えましたが、空のinterfaceクラスは良くないという記事が見つかり、またマーシャリングと組み合わせられるのかわからず断念し、

そこで上述のような静的型mapみたいなことを思いついたわけですが、C#のGenericsだと値は
渡せなかったのを思い出し、諦め、

型のmappingみたいなことをコンパイル時にやることは可能か質問したのですが、無理そうですね・・・。
一般的な実装について聞くには情報不足でした。

iwamoto_takaakiさんの提案していただいた方法はかなりトリッキーとのことですが・・・、私は好きだけどいいんだろうか、こんな設計・・・。

ikuwow👍を押しています

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

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

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

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

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

guest

回答4

0

ベストアンサー

C#言語のジェネリクスでは、C++言語で言うところの非型(non-type)テンプレート・パラメータをサポートしません。

Differences Between C++ Templates and C# Generics (C# Programming Guide)より引用:

In addition, C# does not attempt to provide all of the functionality that C++ templates provide.

The following are the key differences between C# Generics and C++ templates:

  • C# does not allow non-type template parameters, such as template C<int i> {}.
  • C# does not support explicit specialization; that is, a custom implementation of a template for a specific type.
  • C# does not support partial specialization: a custom implementation for a subset of the type arguments.

あとは、KSwordOfHaste さんと同意見です。C++テンプレートとC#ジェネリクスでは設計思想の根本が異なっているため、実際に解決したい課題をより具体化した方が良いと思います。

投稿2017/01/09 11:40

yohhoy

総合スコア6191

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

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

yohhoy

2017/01/12 10:38 編集

質問追記への回答(というよりコメント): C++テンプレートには「コンパイルフェーズでの自動コード生成」という側面があり、要望としてはそれを行いたいと解釈しました。 一方のC#ジェネリクスは「型安全なプログラミングインタフェースの提供」にしかフォーカスしないため、"コンパイル時"や"静的な"という要件にはそぐわない気がします。.NET環境ではC#ソースコード→中間言語(MSIL)に変換するフェーズのほか、MSIL解釈時には改めて一部JITコンパイルされます。そもそもC#ジェネリクスはIL生成時点では展開(インスタンス化)が行われず、型パラメータの適用処理はIL実行時に行われる仕組みのようです。 ソースコード上の冗長記述を避けるためのプログラム構造という意味では、http://ufcpp.net/study/csharp/sp2_generics.html?p=3#pseudo-static も、インタフェースを使った素直な動的ディスパッチ実装も本質的には同じだと思います。前者は「よりオーバーヘッドが小さい実行が行われる可能性がある」という違いでしょうか。
yumetodo

2017/01/14 09:45

なるほど、そういうものと理解することにします。
guest

0

第二引数と戻り値の型しか違わないなら

t4テンプレートでコードを自動生成するのが近いかと。

投稿2017/01/12 10:47

ozwk

総合スコア13521

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

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

0

C#,C++ともに広く言語仕様を知ってはいないので間違いがあったらすみません。個人的には次のように考えます。

コンパイル時に値=>型に変換

C++だと型のバリエーションを扱うテンプレートの引数には値を渡せますがC#ではそれに対応する機能であるジェネリクスの引数へ値を与える機能はないと思います。

(多分ランタイムに型のメタ情報をアクセスしない前提かアクセスできる前提かに違いがあるためにC#の設計者は値を型の引数にすることで、C++でいうテンプレートの特殊化と実体化の仕様をC#に導入したときのメリット・デメリットを考え採用しなかったのだと思います。おそらくランタイムで型のメタ情報をアクセスできれば不要であるという判断だったのではないでしょうか・・・)

C++のテンプレートではSHARED_INFO_0, SHARED_INFO_1, ...に継承関係がないものでも展開できる一種のマクロになっているように(自分は)捉えています。C#では型制約に関する適切な機能を使って設計するということになると思います。

設計の考え方自体が違うので、テンプレートとジェネリクスを漠然と比較すると議論が発散してしまいます。質問としては「この目的でこういう設計をしたいんのだがC++でこう書いたものをC#でどう設計すべきか」のような具体的な問題にフォーカスしたほうがいいと思います。ご質問の内容ではSHARED_INFOを切り替える目的やSHARED_INFO_Xにどんな共通性があってテンプレートにしたいのかが明記されていないので充分フォーカスすることができないと思います。

投稿2017/01/09 08:04

KSwordOfHaste

総合スコア18394

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

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

0

C++の書き方が今ひとつ解りませんでしたが、#IFを使うとプリコンパイルで特定のコードのみを有効にできます。

ところで、例えば呼び出し側でremarkが使えるかどうかはInfoLevel毎に処理を変えないといけませんよね。
呼び出し先で型を知っている状態をC#では嫌います。(この点でC++であっても、クラスを使う理由になりえると思います。)

この点は問題ありませんか?

投稿2017/01/09 07:28

iwamoto_takaaki

総合スコア2883

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

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

yumetodo

2017/01/09 07:38

#ifだと関数(static method)のoverloadができないですよね? 上の例だと f<1>(); f<0>(); のように呼び分けがC++だとできます。 >例えば呼び出し側でremarkが使えるかどうかは ええっとちょっと言いたいことが理解できませんでした。 例えばC++のtemplateでもC#のGenericsでも型名を f<SHARE_INFO_0>(); f<SHARE_INFO_1>(); のように渡すことはできますが、C#のGenericsだとそれはできなかった気がするので、どういうアプローチを取るのだろうと思ったのです。
yumetodo

2017/01/09 07:40

s/C#のGenericsだとそれはできなかった気がするので/C#のGenericsだと数値を渡すことはできなかった気がするので/
iwamoto_takaaki

2017/01/09 08:25

> f<1>(); > f<0>(); そういうことですね。Static Metodを使うのにGenericを使おうとしているのですね。 型の指定に値を使うことは出来ませんが、静的メソッドを使う方法については、ありました。 http://ufcpp.net/study/csharp/sp2_generics.html?p=3 かなりトリッキーな方法ですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問