Choosing which Template class type in main() during runtime

Viewed 74

So I know that templates must be determined during compile time but I'd like to know if there is a way to choose which template object type during runtime in an efficient manner, rather than being redundant by creating a case for each type and copy pasting a lot of code that is only altered to handle each type respectively.

I'd like the user to enter in which type (float, string, int), then have an object BinTree be created for the respective type, then have it be used in a separate switch statement that can handle the object regardless of it's type

int main(int argc, char **argv) {
/***
 *  Read argv for file code here
 **/

bool validInput = true;

enum ValueTypes {aInt, aFloat, aString};
ValueTypes ValueType;
// somehow declare a pointer for the object BinTree<T> here so it is accessable throughout main.

while (validInput)
{
    cout << "Enter list type (i - int, f - float, s - std:string):\t" << endl;
    char charInput;
    printf("Enter a command: ");
    cin >> charInput;

    /**
     * this switch will determine the type and instantiate a bintree object respective to it.
     *
     */
    switch (charInput)
    {
    case 'i':
    {
        printf("This is case i\n");
        BinaryTree<int> BinTree;
        validInput = false;
        ValueType = aInt;
        break;
    }
    case 'f':
    {
        printf("This is case f\n");
        BinaryTree<float> BinTree;
        validInput = false;
        ValueType = aFloat;
        break
    }
    case 's':
    {
        printf("This is case s\n");
        BinaryTree<string> BinTree;
        validInput = false;
        ValueType = aString;
        break;
    }
    default:
    {
        cin.clear();
        cin.ignore();
        cout << "Please enter a valid option." << endl;
        break;
    }
    }
} // while ValidInput

validInput = true;
while (validInput)
{ // while

    /**
     * I am trying to get this while loop and switch statement to use binTree<T> regardless of
     * T's type.
     **/
    
    string userPrompt2 = "these are user commands such as input 'i' to insert item";
    cout << userPrompt2;

    string stringInput;
    float floatInput;
    int intInput;

    char charInput;
    cin >> charInput;
    switch (charInput)
    {
    case 'i':
    {
        /**
         *  These if cases are to handle different types from user input for cin <<
         * 
         */
        cout << "Item to insert: ";
        if (ValueType == aString)
        {
            cin >> stringInput;
            BinTree.Insert(stringInput);
        }
        else if (ValueType == aFloat)
        {
            cin >> floatInput
            BinTree.Insert(floatInput);
        }
        else if (ValueType == aInt)
        {
            cin >> intInput;
            BinaryTree.Insert(intInput);
            
        }

    }
    case 'd':
    {
    }
    case 'r':
    {
    }
    }
} // while

}

1 Answers

Create an abstract base class providing all the operations you want to be available in a type-independent manner, use a template class to implement the operations and dynamically allocate an object of this type on the choice of type.

The following is a simplified example for a class that reads input of either type std::string or int and prints the input to stdout. You could ajust for the object to wrap the BinaryTree and provide like insert, ect.

// abstract wrapper type providing functionality for reading input & printing
struct BaseData
{
    virtual ~BaseData() = default;

    virtual void Read(std::istream& stream) = 0;
    virtual void Print(std::ostream& stream) const = 0;
};

// concrete implementation for a specific type
template<class ValueType>
struct Data : BaseData
{
    void Read(std::istream& stream) override
    {
        stream >> data;
    }

    void Print(std::ostream& stream) const override
    {
        stream << data;
    }

    ValueType data;
};

int main() {
    std::unique_ptr<BaseData> m_data;

    char type;

    std::cin >> type;

    // choose & create the wrapper type
    switch (type)
    {
    case 'i':
        m_data = std::make_unique<Data<int>>();
        break;
    case 's':
        m_data = std::make_unique<Data<std::string>>();
        break;
    default:
        std::cerr << "invalid type\n";
        return 1;
    }

    // use data-type independent operations
    m_data->Read(std::cin);
    m_data->Print(std::cout);
}
Related