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 . I am working with the windows Task Scheduler & its millions of objec开发者_如何学Pythonts it has, ITask...this ITask...that.
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.
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). 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. 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.
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?
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
.
This doesn't really make sense in 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?
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 e.g. 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.
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:
- a single function template
- a refactor into a class template, with the non type specific code in base class methods might work well
- a collection of function some of which are templated with the top-level function being templated
- 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.
精彩评论