用C ++中的静态多态替换动态多态

时间:2017-07-07 08:40:51

标签: variadic-templates c++17 stdtuple static-polymorphism

由于avr-g ++将vtable放在RAM中,我使用静态多态性写了一个替代品,这是事实。

考虑以下示例:

volatile uint8_t x;

struct IX {
    virtual void f() const = 0;
//    virtual ~IX() = default; // need delete
};

struct A : public IX {
    const uint8_t v = 0;
    void f() const override {
        x = v;
    }
};

struct B : public IX {
    const uint8_t v = 1;
    void f() const override {
        x = v;
    }
};

struct C : public IX {
    const uint8_t v = 2;
    void f() const override {
        x = v;
    }
};

volatile uint8_t index = 2;

int main() {
    A a;
    B b;
    C c;
    const std::array<const IX*, 3> cc{&a, &b, &c};

    cc[index]->f();

    while(true) {}
}

此处我们有一些类型ABC实现接口IX并在数组cc中放置指针。然后我们为特定实例调用虚函数f()。 (在像AVR这样的小型μC上使用它,存在RAM的“浪费”,因为vtable被放置在RAM中并且每个对象包含vptr,并且由于f()的间接调用而导致性能损失。

所以我在这种情况下寻找替代解决方案:最简单的方法是使用异构容器,如std::tuple并编写一个switch语句:

const std::tuple<A, B, C> t;

auto f = [](const auto& v) {
    v.f();
};

switch (index) {
case 0:
    f(std::get<0>(t));
    break;
case 1:
    f(std::get<1>(t));
    break;
case 2:
    f(std::get<2>(t));
    break;
default:
    assert(false);
    break;
}

这产生了optimale机器代码,但它是一种不灵活的解决方案。所以我写了一个元函数来调用f()来获取元组的特定元素:

const std::tuple<A, B, C> t;

Meta::visitAt(t, index, [](const auto& v){v.f();});

实现如下:

namespace Meta {
    namespace detail {
        template<uint8_t  N>
        struct visit {
            template<typename T, typename F>
            static void at(T& tuple, uint8_t index, const F& f) {
                if (index == (N - 1)) {
                    f(std::get<N - 1>(tuple));
                }
                else {
                    visit<N - 1>::at(tuple, index, f);
                }
            }

        };
        template<>
        struct visit<0> {
            template<typename T, typename F>
            static void at(T&, uint8_t , const F&) {
                assert(false);
            }
        };

        template<typename T, typename F, size_t... I>
        void all(const T& tuple, const F& f, std::index_sequence<I...>) {
            (f(std::get<I>(tuple)), ...);
        }

    }
    template<typename... T, typename F>
    void visitAt(const std::tuple<T...>& tuple, uint8_t index, const F& f) {
        detail::visit<sizeof...(T)>::at(tuple, index, f);
    }
    template<typename... T, typename F>
    void visitAt(std::tuple<T...>& tuple, uint8_t index, const F& f) {
        detail::visit<sizeof...(T)>::at(tuple, index, f);
    }
    template<typename... T, typename F>
    void visit(const std::tuple<T...>& tuple, const F& f) {
        detail::all(tuple, f, std::make_index_sequence<sizeof...(T)>{});
    }
}

这在我的场景中非常有效,但显然仅限于静态容器(如std::tuple)。还有一个for-each-like迭代Meta::visit()

我的问题是:这种方法还有其他缺点/限制吗?

有任何改进吗?

0 个答案:

没有答案