All streams
Search
Write a publication
Pull to refresh
16
0
Send message
По первому примеру не могу согласиться: не имеет значения в каком порядке будет «вычислен» std::move, он никак не модифицирует передаваемый аргумент, т.к. производится лишь преобразование типа ссылки — в стандарте указано (20.2.5):
template <class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept {
    return static_cast<remove_reference_t<T>&&>(t);
}
Не нравится мне этот подход с индексированием базовых типов и побочным эффектом у оператора приведения типа. Можно сделать перечисление базовых типов в виде SFINAE специализации шаблона. Например:
Код
#include <iostream>
#include <tuple>
#include <typeinfo>
#include <utility>

namespace reflect {

namespace detail {

template <size_t>
struct ubiq {
    template <class T>
    constexpr operator T&() const;
};

template <class T>
struct exact {
    constexpr operator T&() const;
};

template <class>
using void_t = void;

template <class T, size_t N = sizeof( T ), class S = std::make_index_sequence<N>, class = void>
struct tuple_size_helper : tuple_size_helper<T, N - 1, std::make_index_sequence<N - 1>> {
};

template <class T, size_t N, size_t... I>
struct tuple_size_helper<T, N, std::index_sequence<I...>, void_t<decltype( T{ubiq<I>{}...} )>> {
    using type = std::integral_constant<size_t, N>;
};

} // namespace detail

template <class T>
using tuple_size = typename detail::tuple_size_helper<T>::type;

namespace detail {

template <size_t N, class T, class = std::make_index_sequence<N>, class = std::make_index_sequence<tuple_size<T>::value - N - 1>,
          class = void>
struct tuple_element_helper;

template <size_t N, class T, size_t... I, size_t... J>
struct tuple_element_helper<N, T, std::index_sequence<I...>, std::index_sequence<J...>,
                            void_t<decltype( T{ubiq<I>{}..., exact<int>{}, ubiq<J>{}...} )>> {
    using type = int;
};

template <size_t N, class T, size_t... I, size_t... J>
struct tuple_element_helper<N, T, std::index_sequence<I...>, std::index_sequence<J...>,
                            void_t<decltype( T{ubiq<I>{}..., exact<size_t>{}, ubiq<J>{}...} )>> {
    using type = size_t;
};

template <size_t N, class T, size_t... I, size_t... J>
struct tuple_element_helper<N, T, std::index_sequence<I...>, std::index_sequence<J...>,
                            void_t<decltype( T{ubiq<I>{}..., exact<std::string>{}, ubiq<J>{}...} )>> {
    using type = std::string;
};

} // namespace detail

template <size_t N, class T>
struct tuple_element {
    using type = typename detail::tuple_element_helper<N, T>::type;
};

template <size_t N, class T>
using tuple_element_t = typename tuple_element<N, T>::type;

namespace detail {
template <class T, class = std::make_index_sequence<tuple_size<T>::value>>
struct as_tuple_helper;

template <class T, size_t... I>
struct as_tuple_helper<T, std::index_sequence<I...>> {
    using type = std::tuple<tuple_element_t<I, T>...>;
};
} // namespace detail

template <class T>
using as_tuple_t = typename detail::as_tuple_helper<T>::type;

} // namespace reflect

struct s {
    int a;
    size_t b;
    int x;
    std::string q;
};

int main()
{
    std::cout << reflect::tuple_size<s>() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<0, s> ).name() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<1, s> ).name() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<2, s> ).name() << std::endl;
    std::cout << typeid( reflect::tuple_element_t<3, s> ).name() << std::endl;
    std::cout << typeid( reflect::as_tuple_t<s> ).name() << std::endl;
}

Надеемся на C++20, уже и обновленное предложение завезли (P0424R1):
template <typename CharT, CharT const* str, std::size_t length>
auto operator"" _udl();
Сигналы теряются, т.к. при эмиссии нескольких сигналов до вызова метода dbms_alert.waitone, он возвращает только последний. Т.е. если между вызовами waitone было более одного сигнала остальные будут потеряны.
Ну и подписываться/отписываться (dbms_alert.register/remove) на сигнал внутри процедуры не имеет смысла.
С помощью небольшой магии можно сделать лямбды:
template <class T>
struct Eval {
    template <class...>
    struct call {
        using value = T;
    };
};

template <class... T>
struct Apply {
    template <class...>
    struct call;
};

template <class F, class... X>
struct Eval<Apply<F, X...>> {
    template <class...>
    struct call {
        using value = apply<apply<Eval<F>>, apply<Eval<X>>...>;
    };
};

template <class... T>
template <class...>
struct Apply<T...>::call {
    using value = apply<Eval<Apply<T...>>>;
};

template <class F, class X>
struct Substitute {
    template <class...>
    struct call {
        using value = F;
    };
};

template <class X>
struct Substitute<X, X> {
    template <class Y>
    struct call {
        using value = Y;
    };
};

template <template <class...> class F, class... T, class X>
struct Substitute<F<T...>, X> {
    template <class Y>
    struct call {
        using value = F<apply<Substitute<T, X>, Y>...>;
    };
};

template <class X, class F>
struct Lambda {
    template <class Y>
    struct call {
        using value = apply<Eval<apply<Substitute<F, X>, Y>>>;
    };
};

template <class X, class F>
struct Eval<Lambda<X, F>> {
    template <class...>
    struct call {
        using value = Lambda<X, F>;
    };
};

Которые можно применять на месте:

struct X{};
using lambda = Lambda<X, Apply<List, X>>;
using result = apply<lambda, One>; // Cons<One, Nil>


Польный пример
Для себя нашел, что использовать template template для передачи метафункций не удобно, как минимум возникают проблемы с возвратом функций из функций. Вместо этого я в явном виде объявляю метафункцию аппликации:
template <class F, class... X>
using apply = typename F::template call<X...>::value;

А далее все функции имеют вложенный шаблон call:
struct And {
    template <class X, class... Y>
    struct call;
};

template <class X>
struct And::call<X> {
    using value = X;
};

template <class X, class... Y>
struct And::call<Zero, X, Y...> {
    using value = Zero;
};

template <class X, class... Y>
struct And::call<One, X, Y...> {
    using value = apply<And, X, Y...>;
};

Таким образом сами функции более не являются шаблоном от параметров, и ими можно оперировать как другими объектами — положить в список, вернуть в виде результата другой функции и т.п.

Код
#include <iostream>

template <class F, class... X>
using apply = typename F::template call<X...>::value;

template <class T>
struct Identity {
    template <class...>
    struct call {
        using value = T;
    };
};

struct One : Identity<One> {
};

struct Zero : Identity<Zero> {
};

struct Nil : Identity<Nil> {
};

struct Not {
    template <class X>
    struct call;
};

template <>
struct Not::call<Zero> {
    using value = One;
};

template <>
struct Not::call<One> {
    using value = Zero;
};

struct And {
    template <class X, class... Y>
    struct call;
};

template <class X>
struct And::call<X> {
    using value = X;
};

template <class X, class... Y>
struct And::call<Zero, X, Y...> {
    using value = Zero;
};

template <class X, class... Y>
struct And::call<One, X, Y...> {
    using value = apply<And, X, Y...>;
};


template <class H, class T>
struct Cons : Identity<Cons<H, T>> {
};

struct Head {
    template <class>
    struct call;
};

template <class H, class T>
struct Head::call<Cons<H, T>> {
    using value = H;
};

struct Tail {
    template <class>
    struct call;
};

template <class H, class T>
struct Tail::call<Cons<H, T>> {
    using value = T;
};

struct List {
    template <class...>
    struct call;
};

template <>
struct List::call<> {
    using value = Nil;
};

template <class H, class... T>
struct List::call<H, T...> {
    using value = Cons<H, apply<List, T...>>;
};

struct Map {
    template <class T, class F>
    struct call;
};

template <class H, class T, class F>
struct Map::call<Cons<H, T>, F> {
    using value = Cons<apply<F, H>, apply<Map, T, F>>;
};

template <class F>
struct Map::call<Nil, F> {
    using value = Nil;
};


std::ostream& operator<<( std::ostream& os, One )
{
    os << 1;
    return os;
}

std::ostream& operator<<( std::ostream& os, Zero )
{
    os << 0;
    return os;
}

std::ostream& operator<<( std::ostream& os, Nil )
{
    return os;
}

std::ostream& operator<<( std::ostream& os, And )
{
    os << "&&";
    return os;
}

template <class H, class T>
std::ostream& operator<<( std::ostream& os, Cons<H, T> )
{
    os << "{ " << H() << " " << T() << "}";
    return os;
}

int main()
{
    std::cout << One() << std::endl;
    // 1

    std::cout << apply<And, One, One, One, One>() << std::endl;
    // 1

    std::cout << apply<And, One, One, Zero, One>() << std::endl;
    // 0

    using list = apply<List, One, One, Zero, One>;

    std::cout << list() << std::endl;
    // { 1 { 1 { 0 { 1 }}}}

    std::cout << apply<Map, list, Not>() << std::endl;
    //{ 0 { 0 { 1 { 0 }}}}

    std::cout << apply<Map, list, One>() << std::endl;
    //{ 1 { 1 { 1 { 1 }}}}

    std::cout << apply<Map, list, Zero>() << std::endl;
    //{ 0 { 0 { 0 { 0 }}}}


    using list2 = apply<List, list, list>;

    std::cout << apply<Map, list2, Tail>() << std::endl;
    //{ { 1 { 0 { 1 }}} { { 1 { 0 { 1 }}} }}


    std::cout << apply<Map, list2, One>() << std::endl;
    //{ 1 { 1 }}

    std::cout << apply<List, One, And, Zero>() << std::endl;
    //{ 1 { && { 0 }}}
}

На текущем Debian для apt не работает bash-completion из коробки, так что «сокращиние» выходит весьма сомнительным.
Кстати, весьма полезная «оптимизация»: иногда возникает задача создать библиотеку-заглушку для линковки
#define for(...)
#define P(...)
int main() {
    for(;P("\n"),R--;P("|"))for(e=C;e--;P("_"+(*u++/8)%2))P("| "+(*u/4)%2);
}
Но это всё выглядит страшновато и плохо пахнет. Можем ли мы лучше?

Возможно, в рамках C++14 и можем, но я не придумал, как, а скорее нашёл доказательство, что этого сделать нельзя, но поля этой статьи слишком узки для него.


Можем, если вынести враппер на уровень выше:
call_c_func( doWithCallback, foo ).with_callback<&Foo::doFoo>();

Код
#include <tuple>

namespace detail {

template <class T, class = std::tuple<>>
struct pop_back;

template <class... Result>
struct pop_back<std::tuple<void*>, std::tuple<Result...>> {
    using type = std::tuple<Result...>;
};

template <class First, class Second, class... Tail, class... Result>
struct pop_back<std::tuple<First, Second, Tail...>, std::tuple<Result...>>
  : pop_back<std::tuple<Second, Tail...>, std::tuple<Result..., First>> {
};

template <class F>
struct c_func_info;

template <class R, class... Args>
struct c_func_info<R ( * )( Args... )> {
    using result_type = R;
    using args        = std::tuple<Args...>;
};

template <class R, class... Args>
struct c_func_info<R ( & )( Args... )> : c_func_info<R ( * )( Args... )> {
};

} // namespace detail

template <class, class, class, class>
struct Wrapper;

template <class F, class C, class R, class... Args>
struct Wrapper<F, C, R, std::tuple<Args...>> {

    template <R ( C::*Ptr )( Args... ), class... Args2>
    R with_callback( Args2... args2 )
    {
        return f( args2..., []( Args... args, void* userdata ) { return ( static_cast<C*>( userdata )->*Ptr )( args... ); }, c );
    }

    explicit Wrapper( F f_, C* c_ )
      : f( f_ )
      , c( c_ )
    {
    }

    F f;
    C* c;
};

template <class F, class C>
auto call_c_func( F f, C& c )
{
    using func_info     = detail::c_func_info<F>;
    using callback_info = detail::c_func_info<typename std::tuple_element<std::tuple_size<typename func_info::args>::value - 2,
                                                                          typename detail::pop_back<typename func_info::args>::type>::type>;
    using callback_args = typename detail::pop_back<typename callback_info::args>::type;
    return Wrapper<F, C, typename callback_info::result_type, callback_args>{f, &c};
}

//

#include <iostream>

int doWithCallback( int value, int ( *fn )( int, void* ), void* userdata )
{
    return fn( value, userdata ) * 3;
}

struct Foo {
    explicit Foo( int i )
      : m_i( i )
    {
    }

    int m_i;

    int doFoo( int val )
    {
        std::cout << m_i << " vs " << val << std::endl;
        return m_i * 2;
    }
};


int main()
{
    Foo foo{42};
    auto v = call_c_func( doWithCallback, foo ).with_callback<&Foo::doFoo>( 10 );
    std::cout << v << std::endl;
}

Мне тоже, но я еще не придумал как его можно использовать.
Забавно, что схожее предложение на днях было опубликовано в рамках WG21.

С другой стороны данная статья может быть использована как наглядный пример для дальнейшего устранения недостатков лямбд (variadic move-capture, const mutable lambda call, rvalue lambda и т.д.)
Например:
struct functor {
    void operator()( int ) const
    {
        std::cout << "int\n";
    }

    void operator()( std::reference_wrapper<int> ) const
    {
        std::cout << "std::reference_wrapper<int>\n";
    }
};

int k = 10;
part( functor(), k )();
part( functor(), std::ref( k ) )();
Не проще ли сделать так?
template <class F, class... Args>
auto part( F f, Args... args )
{
    return [=]( auto&&... args2 ) {
        return f( args..., std::forward<decltype( args2 )>( args2 )... );
    };
}
Впрочем, можно доработать методом из соседнего комментария
Заголовок спойлера
#include <iostream>
#include <type_traits>
#include <vector>

template <class T>
struct id {
    static id id_;
};

template <class T>
id<T> id<T>::id_;

template <class T>
struct visitor_base {
    using common_type = T;
    virtual void visit_base( T& obj, void* ptr ) = 0;
};

template <class T, class Base = void>
struct visitable : Base {
    void accept( typename Base::visitor_type& obj ) override
    {
        obj.visit_base( static_cast<T&>( *this ), &id<T>::id_ );
    }
};

template <class T>
struct visitable<T, void> {
    using visitor_type = visitor_base<T>;

    virtual void accept( visitor_type& obj )
    {
        obj.visit_base( static_cast<T&>( *this ), &id<T>::id_ );
    }
};

template <class...>
struct tag {
};

template <class T>
struct visitor_impl {
    virtual void visit( T& ) = 0;
};

template <class... Ts>
struct visitor : visitor_impl<Ts>..., std::common_type<Ts...>::type::visitor_type {
    using common_type = typename std::common_type<Ts...>::type::visitor_type::common_type;
    void visit_base( common_type& obj, void* ptr ) override
    {
        visit_( obj, ptr, tag<Ts...>() );
    }

private:
    void visit_( common_type& obj, void*, tag<> )
    {
    }

    template <class Head, class... Tail>
    void visit_( common_type& obj, void* ptr, tag<Head, Tail...> )
    {
        if ( ptr == &id<Head>::id_ ) {
            static_cast<visitor_impl<Head>&>( *this ).visit( static_cast<Head&>( obj ) );
        }
        else {
            visit_( obj, ptr, tag<Tail...>() );
        }
    }
};


struct entity : visitable<entity> {
};

struct geometry : visitable<geometry, entity> {
};

struct model : visitable<model, entity> {
};


struct test_visitor : visitor<entity, geometry, model> {
    void visit( entity& ) override
    {
        std::cout << "entity\n";
    }

    void visit( geometry& ) override
    {
        std::cout << "geometry\n";
    }

    void visit( model& ) override
    {
        std::cout << "model\n";
    }
};

int main()
{
    entity e;
    geometry g;
    model m;

    test_visitor v;

    e.accept( v );
    g.accept( v );
    m.accept( v );

    std::cout << "\n";

    std::vector<entity*> vec{&e, &g, &m};
    for ( auto it : vec ) {
        it->accept( v );
    }
}

Да, без RTTI тогда никак.
Вот, теперь должно быть правильно:
Заголовок спойлера
#include <iostream>
#include <vector>

template <class T>
struct visitor_base {
    template <class R>
    void visit_base( R& obj )
    {
        static_cast<typename R::visitor_type&>( *this ).visit( obj );
    }
};

template <class T>
struct visitor : T::parent_visitor_type {
    virtual void visit( T& ) = 0;
};


template <class T, class Base = void>
struct visitable : Base {
    using parent_visitor_type = typename Base::visitor_type;
    using visitor_type        = visitor<T>;

    void accept( typename Base::visitor_base_type& obj ) override
    {
        obj.visit_base( static_cast<T&>( *this ) );
    }
};

template <class T>
struct visitable<T, void> {
    using parent_visitor_type = visitor_base<T>;
    using visitor_base_type   = visitor<T>;
    using visitor_type        = visitor_base_type;

    virtual void accept( visitor_base_type& obj )
    {
        obj.visit( static_cast<T&>( *this ) );
    }
};

struct entity : visitable<entity> {
};

struct geometry : visitable<geometry, entity> {
};

struct geometry2 : geometry {
};

struct model : visitable<model, geometry2> {
};

struct test_visitor : visitor<model> {
    void visit( entity& obj ) override
    {
        std::cout << "entity\n";
    }

    void visit( geometry& obj ) override
    {
        std::cout << "geometry\n";
    }

    void visit( model& obj ) override
    {
        std::cout << "model\n";
    }
};

int main()
{
    entity e;
    geometry g;
    geometry2 g2;
    model m;

    test_visitor v;

    e.accept( v );
    g.accept( v );
    g2.accept( v );
    m.accept( v );

    std::vector<entity*> vec{&e, &g, &g2, &m};
    for ( auto it : vec ) {
        it->accept( v );
    }
}

Хотя вру, через ссылки на базовые классы работать не будет.
Я это вижу как-то так:
Код
#include <iostream>

template <class T>
struct visitor {
    virtual void visit( T& ) = 0;
};

template <class T, class... Base>
struct visitable : Base... {
    template <class Visitor>
    void accept( Visitor& obj )
    {
        static_cast<visitor<T>&>( obj ).visit( static_cast<T&>( *this ) );
    }
};

struct entity : visitable<entity> {
};

struct geometry : visitable<geometry, entity> {
};

struct model : visitable<model, geometry> {
};

struct test_visitor : visitor<entity>, visitor<geometry>, visitor<model> {
    void visit( entity& obj ) override
    {
        std::cout << "entity\n";
    }

    void visit( geometry& obj ) override
    {
        std::cout << "geometry\n";
    }

    void visit( model& obj ) override
    {
        std::cout << "model\n";
    }
};

int main()
{
    entity e;
    geometry g;
    model m;

    test_visitor v;

    e.accept( v );
    g.accept( v );
    m.accept( v );
}

При наличии режима «капитан очевидность» и root-прав не обязательно полагаться, что пользователь вот так вот просто оставил свои пароли лежать в открытом виде. Можно устроить MITM и выловить эти пароли прямо из трафика, а для случая TLS еще заменить системные сертификаты.

Information

Rating
Does not participate
Location
Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Software Architect
PostgreSQL
C#
C++
Linux
Docker
Kubernetes
High-loaded systems
Designing application architecture
Database design