简体   繁体   English

在运行时确定主要变量类型的函数

[英]A function where the main variables type is determined at run-time

I have a kindof simple problem I dont know how to solve, its from using python & becoming accustomed with working with variables where the data type doesn't matter . 我有一个简单的问题,我不知道如何解决,这是因为使用python并习惯了使用数据类型无关紧要的变量。 I am working with the windows Task Scheduler & its millions of objects it has, ITask...this ITask...that. 我正在使用Windows Task Scheduler及其具有数百万个对象的ITask ...此ITask ...那个。

So I have a function, & depending on the parameter triggerType (an enumeration var), the variable trigger will either be of type ITimeTrigger or IBootTrigger ... ugh this is hard to explain in text, if you look at the code below it will be easy to see what my problem is. 所以我有一个函数,&取决于参数triggerType(枚举变量),变量trigger的类型将为ITimeTrigger或IBootTrigger……这很难用文字解释,如果您看下面的代码,容易看出我的问题是什么。

Its ALOT easier to understand my issue by looking at my example below: 通过查看下面的示例,它很容易理解我的问题:

enum meh { I_WANT_A_INT = 50001, I_WANT_A_FLOAT };

bool foo( meh triggerType )
{    
    switch ( triggerType )
    {
        case I_WANT_A_INT:
        {
             int trigger = 10;
        }
        break;
        case I_WANT_A_FLOAT:
        {
             float  trigger  = 10.111;
        }
        break;
        default:
        {
             double  trigger  = 11;
        }
        break;
    }

    trigger  = 5 * trigger ;   // Compile error because trigger is not declared
    cout <<  trigger  << endl;
}

The solutions I know I can use are: 我知道我可以使用的解决方案是:
- I can overload the function & have one for the ITimeTrigger(int) action & another for IBootTrigger(float). -我可以重载该函数,并为ITimeTrigger(int)操作提供一个功能,为IBootTrigger(float)提供另一个功能。 This is something I really dont want to do because the function is really long with alot of repeating code between them. 我真的不想做这件事,因为该函数真的很长,并且它们之间有很多重复的代码。
- ITimeTrigger & IBootTrigger both inherit from the same object ITrigger, so I could declare the trigger var outside the switch as ITrigger, then cast to the object I need within the switch. -ITimeTrigger和IBootTrigger都从同一对象ITrigger继承,因此我可以将开关外部的触发器var声明为ITrigger,然后将其转换为开关中需要的对象。 This will work now, but when I extend this function to schedule a different kind of task trigger will not inherit from ITrigger (win32 semantics again) so this solution wont work. 现在可以正常工作,但是当我扩展此功能以计划其他类型的任务触发器时,它将不会继承自ITrigger(再次是Win32语义),因此该解决方案将无法工作。

How can I declare the variable trigger (whose data type will be determined at run time) so I can then work with the var later on in the function? 如何声明变量trigger(其数据类型将在运行时确定),以便稍后可以在函数中使用var?

You can use templates to avoid the code duplication. 您可以使用模板来避免代码重复。 For example, the above can be rewritten as: 例如,以上内容可以重写为:

// Common code goes here:
template<typename TriggerType>
void bar(TriggerType trigger)
{
    trigger *= 5;
    std::cout << trigger << std::endl;
}

// Differing code goes here:
void foo(meh trigger_type)
{
    switch (trigger_type) {
    case I_WANT_A_INT:
        bar(10); // invokes the int version
        break;
    case I_WANT_A_FLOAT:
        bar(10.111f); // invokes the float version; note the use of 'f'
        break;
    default:
        bar(11.0); // invokes the double version; note the use of '.0' and lack of 'f'
    }
}

For those types with radically different behavior, you can also have specialized instantiations of bar . 对于那些行为截然不同的类型,您还可以使用bar特殊实例化。

This doesn't really make sense in C++; 在C ++中,这实际上没有任何意义。 types are determined at run-time. 类型在运行时确定。 Whilst you could create a hierarchy of classes that behave similarly to the built-in types, overloading operator* , etc. for them polymorphically, I would question why you want the ability to mix primitive types like this? 尽管您可以创建一个类的层次结构,这些类的行为类似于内置类型,重载operator*等,但它们却是多态的,但我会问您为什么要具有这样的混合基本类型的能力?

If you want different versions of the function for different types, but without code duplication, then you probably want to look at using function templates. 如果您想为不同类型使用不同版本的函数,但又没有代码重复,那么您可能想看看使用函数模板。 See eg http://www.parashift.com/c++-faq-lite/templates.html . 参见例如http://www.parashift.com/c++-faq-lite/templates.html

#include <iostream>
using namespace std;

enum meh { i_want_a_int = 50001, i_want_a_float };

template< class Number >
bool bar( Number trigger )
{
    trigger  *= 5;
    cout <<  trigger  << endl;
    return true;
}

bool foo( meh triggerType )
{    
    switch ( triggerType )
    {
        case i_want_a_int:
            return bar<int>( 10 );
        case i_want_a_float:
             return bar<float>( 10.111f );
        default:
             return bar<double>( 11.0 );
    }
}

int main()
{
    foo( i_want_a_float );
}

By the way, you can greatly reduce the chance of inadvertent text replacement by reserving ALL_UPPERCASE identifiers for macros. 顺便说一句,通过为宏保留ALL_UPPERCASE标识符,可以大大减少无意替换文本的机会。

Cheers & hth, 干杯,

Templates are the likely solution. 模板是可能的解决方案。

Its had to say without seeing more detail, but I note that you say "the function is really long with a lot of repeating code". 它不得不说没有看到更多细节,但是我注意到您说“该函数确实很长,并且包含很多重复代码”。

You can have: 你可以有:

  1. a single function template 单个功能模板
  2. a refactor into a class template, with the non type specific code in base class methods might work well 重构到类模板中,并在基类方法中使用非类型特定的代码可能会很好
  3. a collection of function some of which are templated with the top-level function being templated 函数的集合,其中一些被模板化,而顶层函数被模板化
  4. a top level templated function with overloading of some of the sub-routines. 带有某些子例程的重载的顶层模板函数。

Note you also use templating to map from the enum to the type: 注意,您还使用模板将枚举映射到类型:

enum meh { I_WANT_A_INT = 50001, I_WANT_A_FLOAT, I_WANT_A_DOUBLE };

template<meh = I_WANT_A_DOUBLE>
struct TriggerType
{
    typedef double Type;
};
template<>
struct TriggerType<I_WANT_A_INT>
{
    typedef int Type;
};
template<>
struct TriggerType<I_WANT_A_FLOAT>
{
    typedef float Type;
};

template<class T> void setValue(T& t);

template<> void setValue<double>(double& t) { t = 11;}
template<> void setValue<int>(int& t) { t = 10;}
template<> void setValue<float>(float& t) { t = 10.111f;}

template<class T> 
bool fooTyped()
{
    T trigger;
    setValue(trigger);
    trigger *= 5;
    std::cout <<  trigger  << std::endl; 
    return true;
}

bool foo( meh triggerType )
{    
    bool ret = false;
    switch ( triggerType )
    {
        case I_WANT_A_INT:
        {
            ret = fooTyped<TriggerType<I_WANT_A_INT>::Type>(); ;
        }
        break;
        case I_WANT_A_FLOAT:
        {
            ret = fooTyped<TriggerType<I_WANT_A_FLOAT>::Type>(); ;
        }
        break;
        default:
        {
           ret = fooTyped<TriggerType<I_WANT_A_DOUBLE>::Type>(); ;
        }
        break;
    }
    return ret;
}    

void test ()
{
    foo(I_WANT_A_INT);
    foo(I_WANT_A_FLOAT);
    foo((meh)63);
}

Note the dispatch on the enum mapping to the type; 注意将枚举映射到类型的调度; we need this explicit boiler plate because we can't use a run time value to instantiate the template. 我们需要这个显式样板,因为我们不能使用运行时值来实例化模板。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM