3

多くの場合、異なる値で初期化する必要がある20を超えるフィールドを持つ大きな構造体を使用します。私がinit関数を書くたびに、私は心気症でした。私は常に、値が割り当てられる1つのフィールドを見逃すのではないかと心配していました。そのため、各フィールドを1つずつ確認する必要がありました。

私はこれが嫌いなので、サンプルコードのようなCHECK_VALマクロを使用します。これで、構造体の初期化で1つの項目を見逃した場合、コンパイラはエラーを報告します。

タイプ「Check」の値を使用して、タイプ「int」のエンティティを初期化することはできません。

私の質問:私の問題を助ける他の方法があるかどうか?言語はCおよびC++であり、大きな構造体はPODタイプです。

コードサンプル

#define DOCHECK 1
#if DOCHECK
typedef struct _Check{
    char k;
} Check;
Check g_check = {'c'};
#define CHECK_DEL Check c1234567;
#define CHECK_VAL (g_check)
#else
#define CHECK_DEL
#define CHECK_VAL
#endif

typedef struct _BigStruct{
    int bar;
    int foo;
    /*...*/
    int f99;
    int f100;
    CHECK_DEL;
}BigStruct;

void initBigStruct(BigStruct* p){
    int a,b,c,d;
    a = b = c = d = 0;
    /*
        many other code to caculate the value of a,b,c,d
    */
    {
        BigStruct tmp = {a,b,c,d, CHECK_VAL};
        *p = tmp;
    }
}
4

3 に答える 3

5

言語の観点からすると、おそらくそれほど多くはありません。

ただし、GCCには-Wmissing-field-initializersまさにこの状況に対するフラグがあります。他のコンパイラも同様のものを提供していると思います。

于 2013-01-09T02:44:25.563 に答える
3

C ++について話している場合は、クラスにコンストラクターを記述して、必要なもので初期化することができます。ただし、もちろん、データのPODを解除し、。を使用した構造体の初期化を妨げます{..}

Cの場合、@Pubbyが提案するように初期化された構造体を返すファクトリメソッドを記述できます。

変数の数を数えることが気になる場合は、ラベルを使用したC構造体の初期化のように名前付き初期化を使用できます。それは動作しますが、どのように?ドキュメンテーション?

于 2013-01-09T02:44:47.547 に答える
0

この質問が投稿されてから何年も経って、ネストされた構造体で同じ問題に直面しました。POD構造体の範囲を超えた別の答えを提示します。ただし、データ階層のすべてのフィールドが割り当てられていることを確認するという主要な問題は解決されます。このソリューションは、std::optionalとstd::tupleを使用し、ネストされたデータ階層を処理できます。

検討

struct Aggregate2 {
   struct Aggregate1 {
     int DataType1;
     float DataType2;      
   } aggregate1
   size_t DataType3;
 } aggregate2;

と比較します

using Aggregate1 = std::tuple<std::optional<int>, std::optional<float>>;
using DataType3 = size_t;  
std::tuple<std::optional<Aggregate1>, std::optional<DataType3>> aggregate2;

技術的には、これで目的のデータを保存できますが、取得と設定を読み取ることができず、割り当てのチェックが簡単に自動化されません。

これらの問題は、階層を保持する型の定義が構造体の方法ほど読みにくいというトレードオフによって、ここでほぼ間違いなく解決されます。以下のコードは、MSVCとgccでコンパイルされます。使用方法の詳細な手順が含まれています。

//struct_alternative.h
#include <tuple>
#include <optional>

// C++17 template variable to determine if type is std::tuple.
template <typename T>
constexpr bool isTuple = false;
template<typename ... Types>
constexpr bool isTuple<std::tuple<Types...>> = true;

// Get last type of std::tuple
template<typename ...T>
using LastEntityType = std::tuple_element_t<sizeof...(T) - 1, std::tuple<T...>>;

// Class that inherits all members of D.
// Constructor parses that data tree and throws if any instance of D has unassigned Data::data.
template<typename D>
class AssignedData: public D {
public:
    explicit AssignedData(D&& d) : D(std::move(d)) {
        if constexpr (isTuple<typename decltype(D::data)::value_type>) {
            std::apply([&](auto&&... args) {((args.throwIfNotAssigned()), ...);}, *d.data);
        } else {
            d.throwIfNotAssigned();
        }
    }
};

//
// Data is a template class with capability of storing a hierarchy (tree-like structure) of tuple data.
// The template argument represents the type of the data that is stored in an std::optional<T> 
// It has a set and get functinality. 
// 
// Use as follows:
//
// Define data classes that inherit from Data.
//
// class DataType1 : public Data<int>{};
// class DataType2 : public Data<float>{};
//
// Construct aggregate data types where the template argumets can be a combination of tuples of previously
// defined data types and new data types.
//
// class DataType3 : public Data<size_t>{};
// class Aggregate1 : public Data<std::tuple<DataType1, DataType2>> 
// class Aggregate2 : public Data<std::tuple::<Aggregate1, DataType3>>{};
//
// Create intsances of the Aggregate data type and assign the members.
//
// Arrgregate2 aggregate2;
// aggregate2.set<Aggregate1, DataType1>(1); // Will assigne the value 1 to DataType1 of aggregate2::Aggregate1. 
//
// Create an AssignedData object that guarantees that all members are assigned.
//
// auto assigned = AssignedData(std::move(aggregate)); // Will throw when not all data members are assigned.
//
// Get data member through
//
// int dataType1 = assigned.get<DataType4, DataType1>;
//
template <typename T>
class Data {
public:
    Data() {
        if constexpr(isTuple<T>) {
            // Make sure that all tuples are assigned.
            // If not done, Data::data which is of type std::optional<std::tuple<A, B ...>>
            // can get the tuple members (A, B ...) assigned but the std::optional<std::tuple<A, B...>>
            // will not have a value i.e. is an empty std::optional. This becomes a problem when traversing the Data tree.
            data = std::make_optional<T>();
        }
    }
    
    // Throw if any member of Data::data is not assigned i.e. is an empty optional.
    void throwIfNotAssigned() const {
        if (data.has_value()) {
            if constexpr (isTuple<T>) {
                std::apply([&](auto&&... args) {((args.throwIfNotAssigned()), ...);}, *data);
            }
        } else {
            throw(std::runtime_error("Data::data is not set."));
        }
    }

    // Get value of the data type corresponding to the last element of (First, ...U)
    template <typename First, typename ...U>
    auto get() const {
        if constexpr(isTuple<typename decltype(data)::value_type>) {
            if constexpr (sizeof...(U) > 0) {
                return std::get<First>(*data).template get<U...>();
            } else if (std::get<First>(*data).data.has_value()){
                return std::get<First>(*data).data.value();
            }
        } else if (data.has_value()) {
            return data.value();
        } 
        throw(std::runtime_error("Trying to get a Data::data that is not set."));
    }

    // Set value of the data type corresponding to the last element of (First, ...U)
    template<typename First, typename ...U>
    void set(const typename decltype(LastEntityType<First, U...>::data)::value_type& rhs) {
        if constexpr(isTuple<typename decltype(data)::value_type>) {
            if constexpr (sizeof...(U) > 0) {
                std::get<First>(*data).template set<U...>(rhs);
            } else 
                std::get<First>(*data).data = rhs;
        } else {
            data = rhs;
        }
    }

    std::optional<T> data;
};
于 2022-02-05T13:24:37.497 に答える