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

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

ただいまの
回答率

90.46%

  • C#

    7496questions

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

  • C++

    3639questions

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

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 977

yumetodo

score 2457

C++の場合

struct SHARE_INFO_0 {
    LPWSTR shi0_netname;
};
struct SHARE_INFO_1 {
    LPWSTR shi1_netname;
    DWORD  shi1_type;
    LPWSTR shi1_remark;
};
struct SHARE_INFO_2 {
    LPWSTR shi1_netname;
    DWORD  shi1_type;
    LPWSTR shi1_remark;
    DWORD  shi2_permissions;
    DWORD  shi2_max_uses;
    DWORD  shi2_current_uses;
    LPWSTR shi2_path;
    LPWSTR shi2_passwd;
};

のような構造体群と値を

template<typename ...Args>
struct first_enabled {};

template<typename T, typename ...Args>
struct first_enabled<std::enable_if<true, T>, Args...> { using type = T; };
template<typename T, typename ...Args>
struct first_enabled<std::enable_if<false, T>, Args...>: first_enabled<Args...> {};
template<typename T, typename ...Args>
struct first_enabled<T, Args...> { using type = T; };

template<typename ...Args>
using first_enabled_t = typename first_enabled<Args...>::type;

template<int InfoLevel>
using SHARE_INFO = first_enabled_t<
    std::enable_if<0 == InfoLevel, SHARE_INFO_0>,
    std::enable_if<1 == InfoLevel, SHARE_INFO_1>,
    std::enable_if<2 == InfoLevel, SHARE_INFO_2>
>;

のようにmappingして

template<int infoLevel>
SHARE_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

です。

public static List<SHARE_INFO_2> NetShareEnum_2_wrap(string serverName) {
    var re = new List <SHARE_INFO_2> { };
    Int32 er = 0;
    Int32 tr = 0;
    Int32 resume = 0;
    int res;
    do
    {
        res = NetShareEnum(serverName, 2, out var bufPtr, -1, ref er, ref tr, ref resume);
        if (res != (int)NET_API_STATUS.NERR_Success && res != (int)NET_API_STATUS.ERROR_MORE_DATA) break;
        var infoP = bufPtr;
        for (int i = 0; i < er; ++i)
        {
            re.Add(Marshal.PtrToStructure<SHARE_INFO_2>(infoP));
            infoP = IntPtr.Add(infoP, Marshal.SizeOf<SHARE_INFO_2>());
        }
        NetApiBufferFree(bufPtr);
    } while (res == (int)NET_API_STATUS.ERROR_MORE_DATA);
    return re;
}

これを一般化するために

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

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

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+3

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/12 19:36 編集

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

    キャンセル

  • 2017/01/14 18:45

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

    キャンセル

+1

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/09 16: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だとそれはできなかった気がするので、どういうアプローチを取るのだろうと思ったのです。

    キャンセル

  • 2017/01/09 16:40

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

    キャンセル

  • 2017/01/09 17:25

    > f<1>();
    > f<0>();
    そういうことですね。Static Metodを使うのにGenericを使おうとしているのですね。
    型の指定に値を使うことは出来ませんが、静的メソッドを使う方法については、ありました。

    http://ufcpp.net/study/csharp/sp2_generics.html?p=3

    かなりトリッキーな方法ですね。

    キャンセル

+1

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

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

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.46%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C#

    7496questions

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

  • C++

    3639questions

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