如何做一个类型化参数封装类?

发布于 2024-10-13 12:34:52 字数 842 浏览 1 评论 0原文

我需要在一个类中打包固定数量的任意类型的值。然后我需要能够根据每个参数的类型通过开关传递每个参数。参数的类型是基本的 C 类型和指向东西的指针(特定且有限数量的“东西”),所以没什么复杂的。

该“参数”类需要轻量(在​​空间和处理方面)。

这是我需要如何使用它的示例:

void MyFunc( const Parameters &Params )
{
// for loop
switch( Params(0).GetType() ) {
  case MY_INT_TYPE: int ValInt = Params(0).Get<int>(); ...
  case MY_PTR_TO_MY_STUFF1: MyStuff1 *ValS1 = Params(0).Get<MyStuff1*>(); ...
  ...
  }
}

Parameters MyParams(2);
MyParams.Set<int>(0, 123);
MyParams.Set<MyStuff1*>(1, &SomeClassInstance);
MyFunc( MyParams );
...
MyParams.Set<float>(0, 123.456);  // The same variable in the same scope
MyParams.Set<int*>(1, &Val);
MyFunc( MyParams );

当然,我可以手动专门化所有类型并将其存储在联合中,这就是强力方法。我一直在想有一种更简单的方法可以做到这一点,但无法弄清楚。我可以使用类型特征来存储类型信息,但我对值感到困惑。并且铸造价值不是一个选择。

有指针(比喻指针)吗?

I need to package a fixed number of values of arbitrary types in a class. Then I need to be able to pass each parameter through a switch according the their type. The types of the parameters are basic C types and pointers to stuff (a specific and limited number of "stuff"), so nothing complicated.

That "Parameter" class needs to be light (in space as well as in processing).

This is an example of how I need to use it:

void MyFunc( const Parameters &Params )
{
// for loop
switch( Params(0).GetType() ) {
  case MY_INT_TYPE: int ValInt = Params(0).Get<int>(); ...
  case MY_PTR_TO_MY_STUFF1: MyStuff1 *ValS1 = Params(0).Get<MyStuff1*>(); ...
  ...
  }
}

Parameters MyParams(2);
MyParams.Set<int>(0, 123);
MyParams.Set<MyStuff1*>(1, &SomeClassInstance);
MyFunc( MyParams );
...
MyParams.Set<float>(0, 123.456);  // The same variable in the same scope
MyParams.Set<int*>(1, &Val);
MyFunc( MyParams );

Of course I can specialize for all types manually and store in a union, that is the brute force approach. I keep thinking there is an easier way to do this, but can't figure it out. I can use type traits to store the type info, but I am stuck for the value. And casting the value is not an option.

Any pointer (figurative pointers that is)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吾性傲以野 2024-10-20 12:34:52

关于您最后的评论,我给您下一个答案:)

class ParamsProcessor
{
    // custom parameter reciever for type T1
    template<>
    public ParamsProcessor& in<T1>(const T1& o)
    { ... } 

    // custom parameter reciever for type T2
    template<>
    public ParamsProcessor& in<T2>(T2 t2)
    { ... } 

    // default parameter reciever
    template<class T>
    public ParamsProcessor& operator in(const T& o)
    {
       // some generic way if the project allows
       ...
       return *this
    } 

    void Process()
    { ... }
};


ParamsProcessor pp;
pp.in<T1>(0,123)
  .in<T2>(0,123.456)
  .in("asds");
  .Process();

如果每个重载都能自行处理,那当然更好。
我真心希望我的回答能够满足您的需求。

Regarding last your comment I give you next answer :)

class ParamsProcessor
{
    // custom parameter reciever for type T1
    template<>
    public ParamsProcessor& in<T1>(const T1& o)
    { ... } 

    // custom parameter reciever for type T2
    template<>
    public ParamsProcessor& in<T2>(T2 t2)
    { ... } 

    // default parameter reciever
    template<class T>
    public ParamsProcessor& operator in(const T& o)
    {
       // some generic way if the project allows
       ...
       return *this
    } 

    void Process()
    { ... }
};


ParamsProcessor pp;
pp.in<T1>(0,123)
  .in<T2>(0,123.456)
  .in("asds");
  .Process();

Sure better if each overload will process itself.
I am really hope that my answers will cover your needs.

黑白记忆 2024-10-20 12:34:52

无论如何,如果您不使用运行时多态性,就会立即出现一个切换。如果您将使用静态多态性,那么您需要专门化或切换。

使用 type2enum 和 enum2type 映射的好方法:

type2enum:

// preparition
template<class T> type2enum();
#define TYPE2ENUM_SPEC(TYPE, ENUM) \
template<> type2enum<TYPE>() \
{ return ENUM; }

enum { T1enum, T2enum, T3enum }
TYPE2ENUM_SPEC(type1_t, T1enum);
TYPE2ENUM_SPEC(type2_t, T2enum);
TYPE2ENUM_SPEC(some_third_type_t, T3enum);

和向后 enum2type:

// preparition
template<int Enum>
struct enum2type;
#define ENUM2TYPE_SPEC(ENUM, TYPE) \
template<> struct Enum2Type<ENUM> \
{ typedef TYPE type_t; }


// and generic macro
#define CREATE_TYPE_MAPPING(TYPE, INTEGER) \
#define TYPE2ENUM_SPEC(TYPE, INTEGER) \
#define ENUM2TYPE_SPEC(INTEGER, TYPE)

以及代码示例:

我们有 Type1、Type2、Type3 和 enum: ,

enum {T1, T2, T3};

CREATE_TYPE_MAPPING(Type1, T1);
CREATE_TYPE_MAPPING(Type2, T2);
CREATE_TYPE_MAPPING(Type3, T3);

而不是将其用于将类型映射到 enum:

int typeId = type2enum<Type1>();

或将常量枚举映射到 type:

typedef Enum2Type<ConstEnumType>::type_t some_t;

以及运行时枚举类型你使用类似:

template<class ObjT>
void DoWithTypeIdValue(int enumValue, const ObjT& obj)
{
    switch(enumValue)
    {
       case enumType1:
           obj.do<Enum2Type<enumType1>::type_t>();
           break;
       case enumType2:
           obj.do<Enum2Type<enumType2>::type_t>();
           break;
       case enumType3:
           obj.do<Enum2Type<enumType3>::type_t>();
           break;
       default:
           assert(!"Unknown type constant");
    }
}

Anyway there will be at once one switch if you will not use run-time polymorphism. If you will use static polymorphism than you need either specialization or switch.

Its good approach to use type2enum and enum2type mapping:

type2enum:

// preparition
template<class T> type2enum();
#define TYPE2ENUM_SPEC(TYPE, ENUM) \
template<> type2enum<TYPE>() \
{ return ENUM; }

enum { T1enum, T2enum, T3enum }
TYPE2ENUM_SPEC(type1_t, T1enum);
TYPE2ENUM_SPEC(type2_t, T2enum);
TYPE2ENUM_SPEC(some_third_type_t, T3enum);

and backward enum2type:

// preparition
template<int Enum>
struct enum2type;
#define ENUM2TYPE_SPEC(ENUM, TYPE) \
template<> struct Enum2Type<ENUM> \
{ typedef TYPE type_t; }


// and generic macro
#define CREATE_TYPE_MAPPING(TYPE, INTEGER) \
#define TYPE2ENUM_SPEC(TYPE, INTEGER) \
#define ENUM2TYPE_SPEC(INTEGER, TYPE)

and the sample of code:

we have Type1, Type2, Type3 and enum:

enum {T1, T2, T3};

CREATE_TYPE_MAPPING(Type1, T1);
CREATE_TYPE_MAPPING(Type2, T2);
CREATE_TYPE_MAPPING(Type3, T3);

than use it for mapping type to enum:

int typeId = type2enum<Type1>();

or constant enum to type:

typedef Enum2Type<ConstEnumType>::type_t some_t;

and for runtime enum to type you use something like:

template<class ObjT>
void DoWithTypeIdValue(int enumValue, const ObjT& obj)
{
    switch(enumValue)
    {
       case enumType1:
           obj.do<Enum2Type<enumType1>::type_t>();
           break;
       case enumType2:
           obj.do<Enum2Type<enumType2>::type_t>();
           break;
       case enumType3:
           obj.do<Enum2Type<enumType3>::type_t>();
           break;
       default:
           assert(!"Unknown type constant");
    }
}
抹茶夏天i‖ 2024-10-20 12:34:52

允许摆脱 switch 的另一种方法是使用枚举和函子数组:

enum TheTypes { T1, T2, T3, TheTypesCount};

boost::function<void (const Parameters &Params)> Processors[] = {
    boost::bind(&T1::ProcessingMethod, t1Obj, _1),
    boost::bind(&T2::ProcessingStaticMethod, _1),
    boost::bind(RawFunction, _1)
};

这允许您用简单的构造替换侵入式缝合:

void MyFunc( const Parameters &Params )
{
    // for loop
    Processors[Params(0).GetType()](Params(0));
}

恭喜!

如果您是疯子,那么将讨论下一种方法。
创建静态装饰器(mixin)来自动执行以下操作怎么样:

  • 索引特征
  • 存储特征处理函子
  • 形成特征数组

Another way that allows to get rid of switch is use enum and array of functors:

enum TheTypes { T1, T2, T3, TheTypesCount};

boost::function<void (const Parameters &Params)> Processors[] = {
    boost::bind(&T1::ProcessingMethod, t1Obj, _1),
    boost::bind(&T2::ProcessingStaticMethod, _1),
    boost::bind(RawFunction, _1)
};

And that allows you substitute intrusive stitch with simple construct:

void MyFunc( const Parameters &Params )
{
    // for loop
    Processors[Params(0).GetType()](Params(0));
}

Congratulaions!

If you are maniac than will discuss next approach.
What about creating static decorator (mixin) for automaticially do following:

  • indexing features
  • store feature processing functor
  • forming an array of features
飞烟轻若梦 2024-10-20 12:34:52

理想的实现将使用由有效参数类型集参数化的变体类型(例如boost::variant)。以下是如何使用变体类型来解决问题的示例:

typedef Variant<int, MyStuff1*> Parameter;

int main()
{
    MyStuff1 SomeClassInstance;

    Parameter MyParams[2] = { 123, &SomeClassInstance };

    int ValInt = MyParams[0].Get<int>();
    MyStuff1* ValS1 = MyParams[1].Get<MyStuff1*>();
}

如果您无法使用 boost,这里是如何实现变体类型的示例。

#include <type_traits>

template<typename T, typename U>
struct MaxSize
{
    static const size_t value = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U);
};

struct ValueBase
{
    virtual ~ValueBase()
    {
    }
};

template<typename T>
struct ValueGeneric : ValueBase
{
    T value;
    ValueGeneric(const T& value) : value(value)
    {
    }
};

template<typename T1, typename T2>
class Variant
{
    typename std::aligned_union<MaxSize<T1, T2>::value, T1, T2>::type storage;

    Variant(const Variant&); // not copyable
    Variant operator=(const Variant&); // not assignable
public:
    template<typename T>
    Variant(const T& value)
    {
        new(&storage) ValueGeneric<T>(value);
    }
    ~Variant()
    {
        static_cast<ValueBase*>(static_cast<void*>(&storage))->~ValueBase();
    }

    void Set(const T1& value)
    {
        SetImpl(value);
    }
    void Set(const T2& value)
    {
        SetImpl(value);
    }
    void Get(const T1& value)
    {
        this->~Variant();
        new (this) Variant(value);
    }
    template<typename T>
    bool IsA() const
    {
        return typeid(T) == typeid(*static_cast<const ValueBase*>(static_cast<const void*>(&storage)));
    }
    template<typename T>
    T& Get()
    {
        assert(IsA<T>());
        return *static_cast<T*>(static_cast<void*>(&storage));
    }
    template<typename T>
    const T& Get() const
    {
        assert(IsA<T>());
        return *static_cast<const T*>(static_cast<const void*>(&storage));
    }

private:
    template<typename T>
    void SetImpl(const T& value)
    {
        this->~Variant();
        new (this) Variant(value);
    }
};

此实现旨在作为示例,仅支持两种类型 - 扩展以支持更多类型相对容易。您可能希望使用模板参数包和移动构造函数来实现最干净、最高效的实现。

An ideal implementation would use a variant type (e.g. boost::variant) parameterised by the valid set of parameter types. Here's an example of how to use a variant type to solve the problem:

typedef Variant<int, MyStuff1*> Parameter;

int main()
{
    MyStuff1 SomeClassInstance;

    Parameter MyParams[2] = { 123, &SomeClassInstance };

    int ValInt = MyParams[0].Get<int>();
    MyStuff1* ValS1 = MyParams[1].Get<MyStuff1*>();
}

And here's an example of how to implement a variant type, if you can't use boost.

#include <type_traits>

template<typename T, typename U>
struct MaxSize
{
    static const size_t value = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U);
};

struct ValueBase
{
    virtual ~ValueBase()
    {
    }
};

template<typename T>
struct ValueGeneric : ValueBase
{
    T value;
    ValueGeneric(const T& value) : value(value)
    {
    }
};

template<typename T1, typename T2>
class Variant
{
    typename std::aligned_union<MaxSize<T1, T2>::value, T1, T2>::type storage;

    Variant(const Variant&); // not copyable
    Variant operator=(const Variant&); // not assignable
public:
    template<typename T>
    Variant(const T& value)
    {
        new(&storage) ValueGeneric<T>(value);
    }
    ~Variant()
    {
        static_cast<ValueBase*>(static_cast<void*>(&storage))->~ValueBase();
    }

    void Set(const T1& value)
    {
        SetImpl(value);
    }
    void Set(const T2& value)
    {
        SetImpl(value);
    }
    void Get(const T1& value)
    {
        this->~Variant();
        new (this) Variant(value);
    }
    template<typename T>
    bool IsA() const
    {
        return typeid(T) == typeid(*static_cast<const ValueBase*>(static_cast<const void*>(&storage)));
    }
    template<typename T>
    T& Get()
    {
        assert(IsA<T>());
        return *static_cast<T*>(static_cast<void*>(&storage));
    }
    template<typename T>
    const T& Get() const
    {
        assert(IsA<T>());
        return *static_cast<const T*>(static_cast<const void*>(&storage));
    }

private:
    template<typename T>
    void SetImpl(const T& value)
    {
        this->~Variant();
        new (this) Variant(value);
    }
};

This implementation is intended as an example and only supports two types - it would be relatively easy to extend to support more. You would want to use template-parameter-packs and move-constructors to achieve the cleanest and most efficient implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文