0

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 objects 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?

Sascha
  • 41
  • 7
  • Why are you thinking in Python when you're programming in C++? Get a [C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list), so you can learn how to do things properly within the language. – GManNickG Jan 18 '11 at 00:02

4 Answers4

7

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.

Dawson
  • 2,673
  • 1
  • 16
  • 18
0

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.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • I have a function ScheduleTask( triggerType );, depending on triggerType, I will want to schedule a task to trigger at startup, at a specific time, every week or every month. For each of those different actions, they require a different trigger object (ITimerTrigger, IBootTrigger) BUT they are used the same way, so I will always be saying trigger->put_Delay( "" ); regardless of what type it is – Sascha Jan 17 '11 at 23:12
  • @Sascha: Oh, right. So the primitive types (`int`, `float`, etc.) in your sample code weren't really representative of your actual problem? Instead, you have `ITimerTrigger`, `IBootTrigger` etc.? In which case, I would recommend editing your question to make your code closer to the *actual* situation. – Oliver Charlesworth Jan 17 '11 at 23:14
  • Yeah sorry those were just examples. Is the only way to do this by templates? – Sascha Jan 17 '11 at 23:27
  • @Sascha: Given that your example code is not the same as your actual scenario, I don't really know what you want to do. The answer may be templates, or it may be run-time polymorphism. It's impossible to say without a better description of the problem! – Oliver Charlesworth Jan 17 '11 at 23:30
0
#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,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

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.

Keith
  • 6,756
  • 19
  • 23