Использование общего программирования на C ++ для выполнения вызова func (x, y) во время выполнения для всех перестановок типов x и y - PullRequest
2 голосов
/ 08 ноября 2019
env->CallObjectMethod(hashMapInstance, put, _, _)

Я хочу вызвать эту функцию со всеми возможными перестановками std::string, int, double на подчеркиваниях. Например:

env->CallObjectMethod(hashMapInstance, put, myString, myInt)
env->CallObjectMethod(hashMapInstance, put, myInt, myDouble)
env->CallObjectMethod(hashMapInstance, put, myInt, myInt)
//...

Конечно, я могу сделать это с помощью вложенных ifs, но я буду многократно использовать один и тот же код во многих местах. В идеале я хотел бы получить способ std::map<JavaObject, JavaObject> myMap, а затем для каждой пары на карте сделать следующее:

for (auto pair : myMap)
    if (pair.first.type == JavaObject::String && pair.second.type == JavaObject::Integer)
        env->CallObjectMethod(hashMapInstance, put, pair.first.getString(), pair.second.getInt()) 
    //OR
    if (pair.first.type == JavaObject::Integer && pair.second.type == JavaObject::Integer)
        env->CallObjectMethod(hashMapInstance, put, pair.first.getInt(), pair.second.getInt()) //
    //OR
    if (pair.first.type == JavaObject::Double && pair.second.type == JavaObject::String)
        env->CallObjectMethod(hashMapInstance, put, pair.first.getDouble(), pair.second.getString()) 
    //...

, как вы видите, мне нужен способ, чтобы эффективно звонитькаждая перестановка env->CallObjectMethod(hashMapInstance, put, _, _) для каждой возможной перестановки из JavaObject, JavaObject получено (JavaObject - это просто класс, который может содержать string, int, double и, возможно, больше в будущем)

Первым делом я подумал о создании шаблонной функции:

template<typename T, typename V>
void myCallobjectMethod(env, jobject instance, jmethodID method, T obj1, V obj2)

Но мне все равно нужно прочитать JavaObject.type для первого элемента, а затем внутри этого if, сделать другое if для второй части, просто вызватьмоя шаблонная функция, так что я все еще с той же проблемой.

Я подумал о другом способе, в псевдокоде:

using namespace std::placeholders; 
for (auto pair : myMap)
    auto bind1 = std::bind(env->CallObjectMethod, hashMapInstance, put, _3, _4); //binds the hashMapInstance and put
    auto bind2 = std::bind(bind1, pair.first, _2); //binds the first object
    auto bind3 = std::bind(bin2, pair.second); //binds the second object
bind3(); //now I can call bind3 to execute the call 

но это не так просто, я даже не знаючто происходит с типами вещей здесь.

Ответы [ 2 ]

8 голосов
/ 08 ноября 2019

JavaObject является типом суммы, поэтому он должен иметь функцию visit:

template<class F> auto visit(F&& f, JavaObject const& o) {
    switch (o.type) {
        case JavaObject::String : return f(o.getString());
        case JavaObject::Integer : return f(o.getInt());
        case JavaObject::Double : return f(o.getDouble());
    }
}

Любая унарная visit функция может быть составлена ​​сама для работы с 2 аргументами:

template<class F> auto visit(F&& f, JavaObject const& o1, JavaObject const& o2) {
    return visit([&](auto const& x1) {
        return visit([&](auto const& x2) {
            return f(x1, x2); }, o2); }, o1);
}

Теперь вы можете написать:

visit([&](auto const& x1, auto const& x2) {
    env->CallObjectMethod(hashMapInstance, put, x1, x2);
}, pair.first, pair.second);

Расширение visit до произвольной арности оставлено в качестве упражнения для читателя (Подсказка: используйте рекурсию).

2 голосов
/ 08 ноября 2019

В дополнение к ответу ecatmur, я собираюсь дать подсказку о том, как сделать перестановку:

#include <functional>
#include <iostream>
#include <tuple>

void test(int a, int b, int c, int d, int e, int f, int g) {
    std::cout << a << " " << b << " " << c << " " << d << " " << e << " " << f << " " << g << std::endl;
}

template <typename ReturnT, typename T, typename U, typename ... TRestArgs>
auto easy_bind(std::function<ReturnT(T, TRestArgs...)> func, U&& arg) -> std::function<ReturnT(TRestArgs...)> {
    if constexpr (std::is_same_v<void, ReturnT>) {
        return [=](TRestArgs... args) { func(arg, args...); };
    }
    else {
        return [=](TRestArgs... args) { return func(arg, args...); };
    }
}

template <typename CallableT, typename ... TArgs, std::size_t ... SeqLeft, std::size_t ... SeqRight>
void permute_call_impl(CallableT func, std::tuple<TArgs...> args, std::index_sequence<SeqLeft...>, std::index_sequence<SeqRight...>);

template <typename CallableT, typename ... TArgs, std::size_t ... Seq>
void permute_call_splitter(CallableT func, std::tuple<TArgs...> args, std::index_sequence<Seq...>);

template <typename CallableT, typename ... TArgs, std::size_t ... SeqLeft, std::size_t ... SeqRight>
void permute_call_impl(CallableT func, std::tuple<TArgs...> args, std::index_sequence<SeqLeft...>, std::index_sequence<SeqRight...>) {
    if constexpr (sizeof...(SeqLeft) + sizeof...(SeqRight) == 0u) {
        func();
    }
    else {
        permute_call_splitter(func,
            std::make_tuple(std::get<SeqLeft>(args)..., std::get<SeqRight + sizeof...(SeqLeft) + 1>(args)...),
            std::make_index_sequence<sizeof...(SeqLeft) + sizeof...(SeqRight)>{}
        );
    }
}

template <typename CallableT, typename ... TArgs, std::size_t ... Seq>
void permute_call_splitter(CallableT func, std::tuple<TArgs...> args, std::index_sequence<Seq...>) {
    constexpr auto size = sizeof...(Seq);
    (permute_call_impl(easy_bind(func, std::get<Seq>(args)),
        args,
        std::make_index_sequence<Seq>{},
        std::make_index_sequence<size - 1u - Seq>{}
    ), ...);
}

template <typename CallableT, typename ... TArgs>
void permute_call(CallableT func, TArgs... args) {
    permute_call_splitter(func, std::make_tuple(args...), std::make_index_sequence<sizeof...(TArgs)>{});
}

int main() {
    std::function func = test;
    permute_call(func, 1, 2, 3, 4, 5, 6, 7);
    return 0;
}

Таким образом, в основном у вас есть std::index_sequence, вы выполняете итерацию по каждому индексу, удаляете элемент, объедините оставшиеся две части и передайте его на следующий уровень рекурсии.


Пока я не позаботился о переадресации ссылок в этом примере. Но это даст вам приблизительное представление о том, как все должно работать.

Живой пример

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...