Home > Mobile >  cpp vector type determined by other variable
cpp vector type determined by other variable

Time:08-10

Say I have two method to process values, MethodA and MethodB; I have a string to determine which method will be used, choice. They are all inside a class, and choice is a class argument.MethodA and MethodB is a class which has a method to process one item .

class MethodA
{
public:
    void push(int &X)
    {
        X  = 1;
    }
};

The question is I need to make a vector of method to make computations, but the method is determined by a string choice. The demo code should be like this

class Example
{
public:
    const int vector_size = 10;
    std::string choice;
    std::vector<MethodA *if choice == "A" elses MethodB *> methods;

public:
    Example(std::string method)
    {
        choice = method;
        for (size_t ii = 0; ii < vector_size;   ii)
        {
            methods.push_back(choice == "A" ? new MethodA * : new MethodB *);
        }
    }

    void process(std::vector<float> value)
    {
        for (size_t ii = 0; ii < value.size();   ii)
        {
            methods[ii]->Push(value[ii]);
        }
    }
};

The problem is that how should I properly declare the method vector

std::vector<MethodA *if choice == "A" elses MethodB *> methods;

Thanks a lot!

CodePudding user response:

In this case you could solve this either with inheritance or with a std::variant. This will allow you to store either an instance of MethodA or MethodB in the vector.

Inheritance

#include <vector>
#include <memory>
#include <iostream>

struct Base {
    virtual void push(int& x) const = 0;
    virtual ~Base() = default;
};

struct MethodA : public Base {
    void push(int& x) const override {
        x  = 1;
    }
};

struct MethodB : public Base {
    void push(int& x) const override {
        x  = 20;
    }
};

int main() {
    std::vector<std::unique_ptr<Base>> methods{};
    methods.push_back(std::make_unique<MethodA>());
    methods.push_back(std::make_unique<MethodB>());

    int initial = 1;
    for (const auto& method : methods) {
        method->push(initial);
        std::cout << initial << '\n';
    }
}

Variant

If you use a std::variant take a look at the documentation for it. There are other free functions available for querying what type is currently held and for getting the instance (like std::get). Also, the visitor pattern can be used to clean this up further.

#include <vector>
#include <iostream>
#include <variant>

struct MethodA {
    void push(int& x) const {
        x  = 1;
    }
};

struct MethodB {
    void push(int& x) const {
        x  = 20;
    }
};

using MethodTypes = std::variant<MethodA, MethodB>;

int main() {
    std::vector<MethodTypes> methods{};
    methods.push_back(MethodA{});
    methods.push_back(MethodB{});

    int initial = 1;
    for (const auto& method : methods) {
        if (auto* a = std::get_if<MethodA>(&method)) {
            a->push(initial);
            std::cout << initial << '\n';
        }   
        else if (auto* b = std::get_if<MethodB>(&method)) {
            b->push(initial);
            std::cout << initial << '\n';
        }      
    }
}

CodePudding user response:

This is a classic issue with C . As one commenter mentioned, C is statically typed so all types of things must be known at compile-time. However, there are two typical ways around that.

Method 1: Polymorphism

This is a more Java-like solution and will be every so very slightly slower at run-time. I am using the smart pointer type std::unique_ptr here so there is no manual memory deallocation needed.

#include <memory>
#include <vector>
#include <string>

class MethodBase {
    public:
        virtual void push(int&) = 0;
        virtual ~MethodBase() { }
};

class MethodA :public MethodBase {
    public:
        void push(int& X) override {
            X  = 1;
        }
};

// similar for MethodB

void make_vector(std::string choice) {
    std::vector<std::unique_ptr<MethodBase>> methods;
    if (choice == "A") {
        methods.push_back(std::make_unique<MethodA>());
    }
    else { /* ... deal with other method choices ... */ }
    
    // now you can call anything in the MethodBase interface
    int x = 100;
    methods.front()->push(x);
}

Method 2: Templates

C templates allow you to easily compile multiple "copies" of any function or class, on-demand as needed. Then you would need some conditional statement elsewhere to decide which "copy" to actually use.

This way will be ever so slightly faster than polymorphism at runtime, with no dynamic memory used, but it's also less flexible because you don't really get one variable that can be a vector of either variety.

class MethodA {
    public:
        void push(int& X) {
            X  = 1;
        }
};

// similar for MethodB

template <typename C>
void make_vector() {
    std::vector<C> methods;
    // populate the vector
    methods.emplace_back(); // constructs a new method object in-place
    // now use it...
    int x = 100;
    methods.front().push(x);
}

int main() {
    std::string choice;
    std::cin >> choice;
    if (choice == "A") {
        make_vector<MethodA>();
    }
    else { /* other cases for MethodB etc */ }
}
  •  Tags:  
  • c
  • Related