74902


C言語で実装済みのコード(変更不可)に対し、C++で汎用的な処理を書く方法を検討しています。

たとえばC側に下記のような構造体定義があった場合に、

[code]

    typedef struct
    {
        int     nvalue;
        float   fvalue;
    } Test_t;
    
[/code]

C++で下記のCreate_strucure/Get_memberを実現する方法はありますでしょうか。

[code]

    auto sparam = Create_structure("Test_t");
    auto* pvalue_1 = Get_member(&sparam, "nvalue");
    *pvalue_1 = 1;
    auto* pvalue_2 = Get_member(&sparam, "fvalue");
    *pvalue_2 = 2.3;
    
    //Create_strucure : 文字列で指定されたstruct型を返却
    //Get_member：引数1で指定された変数のメンバ(引数1.引数2)を返却
    
[/code]

目的は、膨大な数のstuructに対して汎用的に使用可能な処理を書くことです。  
struct/member参照を文字列で行うことができれば楽だと思うのですが、そういったことは可能でしょうか。

よろしくお願いいたします。


CC BY-SA 4.0
2021-03-26T01:15:31.777
74896
2021-03-28T00:56:37.140
2021-03-26T02:28:20.073
3060
44508
question
c++
structのメンバを文字列で指定して参照したい


変更不可の実装済みのコードがコンパイル済みライブラリとして提供されていて、Test_tも不透明型として提供されている場合は、情報が失われているので不可能です。

ヘッダーファイルはあるけれど変更はできないという状況でしたら、事前に`clang -Xclang -ast-
dump=json`でヘッダーを解析しASTをファイルにダンプしておいて、実行時にJSONから読みだして引き当てることは原理的には可能だと思います。

実際に Clang libtooling で同様のことをやっているツール＆ライブラリがありました。  
<https://github.com/mlomb/MetaCPP>

コメントにある「C++でもGetComponentがしたい！」もVisualStudioでの似たような話のようです。


CC BY-SA 4.0
2021-03-26T09:09:02.403
74902
2021-03-26T09:09:02.403
241
74896
answer


既に回答がある通り、C言語・C++言語どちらもコンパイルを行うとその時点でポインターやオフセットの形でコード生成され、型情報は削除されます。そのため、実行時にメンバー名でアクセスすることはできません。

2020/12/11にリリースされたBoost Version
1.75（現時点での最新版）から[PFRというヘッダーオンリーライブラリ](https://www.boost.org/doc/libs/1_75_0/doc/html/boost_pfr.html)が追加されています。  
このライブラリを使うと、メンバー名でアクセスすることはできませんが、[インデックスでアクセスする](https://www.boost.org/doc/libs/1_75_0/doc/html/boost_pfr/tutorial.html#boost_pfr.tutorial.accessing_structure_member_by_in)ことは可能です。

[code]

    auto sparam = Test_t{};
    auto& value_1 = boost::pfr::get<0>(sparam);
    value_1 = 1;
    auto& value_2 = boost::pfr::get<1>(sparam);
    value_2 = 2.3;
    
[/code]

* * *

>
> Cコードに対してC++でテストコードを書きたいというのが目的です。色んな構造体に対してあらゆる値をセットするためのコードをいちいち書くのが大変→CSV等で構造体/member名を記述しておいて、テストコードは読み込んだCSVを元に構造体/memberを作成するだけにできればメンテが楽なのでは？

既にコメントの内容の表現を変えただけになりますが、テストデータを書くCSVにはCコードにどのようなメンバーがいるかを知っていなければなりませんし、C++コードはCSVにどのようなデータ型が出現するかを知っていなければなりません。CSVに未知のデータ型を書かれてもC++コードは処理できないわけです。  
また、C++コードはCSVを処理しなければなりません。`int`なら`scanf("%d")`、`float`なら`scanf("%f")`等々、テストドライバー側の負担が大きく、テストが期待通りに動かないリスクも高まるので、あまりお勧めできないアプローチかなと思います。

なるべくシンプルにC++コードで、記述することを提案します。  
例えば、C++20で導入された[指示付き初期化](https://cpprefjp.github.io/lang/cpp20/designated_initialization.html)という記述を使うと、メンバー名付きで初期化できます。

[code]

    Test_t patterns[] = {
      { .nvalue = 1, .fvalue = 2.3, },
      { .nvalue = 2, .fvalue = 4.6, },
    };
    
[/code]

またC++17で導入された[`std::size`](https://cpprefjp.github.io/reference/iterator/size.html)を使うと配列の要素数も取得できます（`std::size(patterns)`は`2`となる）。上記データ配列を別ファイルに切り出し、C++コードに`#include
""`すれば、テストコードとテストデータを分離できます。


CC BY-SA 4.0
2021-03-26T20:07:53.690
74926
2021-03-28T00:56:37.140
2021-03-28T00:56:37.140
4236
4236
74896
answer