The Real Std :: Aplicar -- ++ campo con template-meta-programming campo con c++17 camp codereview Relacionados El problema

The real std::apply


6
vote

problema

Español

Motivación:

ob_*3 toma una sola tupla como argumento y desempaqueta la tupla en la llamada de la función. La limitación es que uno tiene que saber si el argumento es tuple o no, lo que hace que sus casos de uso se separen.

Encontré una manera de resolver el problema con una compatibilidad al 100% hacia atrás (al menos lo creo). El código a continuación no rompió ninguno de los puntos de referencia que he escrito antes, utilizando Benchmark V2, por lo que se prueba:

Código:

  ob_*4  

El uso es el mismo a partir de ob_*5 ( 99887766555443316 es una tupla de funciones que se deben comparar):

  ob_*7  

Algunas pruebas más simples:

  ob_*8  

La solución se basa en esta pregunta < / a>.

Original en ingles

Motivation:

std::apply takes single tuple like argument and unpacks the tuple into the function call. The limitation is that one has to know if the argument is tuple like or not, which makes its use cases severed.

I found a way to solve the problem having 100% backwards compatibility (at least I believe so). The code below didn't break any of the benchmarks I've written before, using benchmark v2, so it is tested:

Code:

template <typename T> struct is_straight_tuple : public std::false_type {};  template <typename ... Ts> struct is_straight_tuple<std::tuple<Ts...>> : public std::true_type {};  template <typename T> struct is_std_array : public std::false_type {};  template <typename T, std::size_t size> struct is_std_array<std::array<T, size>> : public std::true_type {};  template <typename T> struct is_tuple_like : public std::bool_constant<is_std_array<T>::value || is_straight_tuple<T>::value> {};  namespace detail {     template <typename Callable, typename Tuple>     decltype(auto) genuine_apply(Callable&& callable, Tuple&& tuple, std::true_type)     {         return std::apply(std::forward<Callable>(callable), std::forward<Tuple>(tuple));     }      template <typename Callable, typename Tuple>     decltype(auto) genuine_apply(Callable&& callable, Tuple&& tuple, std::false_type)     {         return std::forward<Callable>(callable)(std::forward<Tuple>(tuple));     } }  template <typename Callable, typename T> decltype(auto) genuine_apply(Callable&& callable, T&& argument) {     return detail::genuine_apply(std::forward<Callable>(callable), std::forward<T>(argument),                                  is_tuple_like<std::decay_t<T>>{}); }; 

The usage is the same as of std::apply<>() (callables is a tuple of functions to be benchmarked):

auto callable_input = gen(input); //separate input creation from benchmark auto start = std::chrono::high_resolution_clock::now(); shino::genuine_apply(std::get<Index>(callables), callable_input); auto end = std::chrono::high_resolution_clock::now(); 

Some simpler tests:

#include <stdexcept>  int dummy_x(const std::tuple<int, int>&) {     return 1; }  int dummy_y(int y) {     return y; }  void check(int retvalue, int expectedvalue) {     if (retvalue != expectedvalue)     {         throw std::logic_error("genuine apply doesn't return the correct value");     }   }  int main() {     int res = 0;     res = shino::genuine_apply(dummy_x, std::make_tuple(std::tuple<int, int>(1, 1)));     check(res, 1);      res = shino::genuine_apply(dummy_y, 1);     check(res, 1);      shino::genuine_apply(dummy_y, std::make_tuple(1));     check(res, 1); } 

The solution is based on this SO question.

        

Lista de respuestas

8
 
vote

Creo que hay dos fallas con esta función; El primero está en concepción, y el segundo está en la implementación.

El problema de la concepción es esto: ¿Qué hace genuine_apply realmente? Lo pasas un llamado y un argumento. Si el argumento es un objeto similar a la tupla, lo expandirá y pasa sus elementos como argumentos separados a los que se pueden llamar. Si lo pasa un argumento no tan tuple, pasa el argumento directamente al llamado. ¿Cuáles son las circunstancias en las que quieres ese comportamiento? Piense en apply como ayuda a programadores genéricos; Saben si están pasando un argumento similar a la tupla a public static SoilTypes Default = ...0 porque construyen el argumento . Normalmente, no pasaría los argumentos proporcionados por el usuario directamente a public static SoilTypes Default = ...1 , construiría el argumento usted mismo de los argumentos proporcionados por el usuario. Aquí, obtendrá un comportamiento diferente según el usuario de si el usuario pasa un 99887766655443312 o un public static SoilTypes Default = ...3 . En resumen, hace que sea muy difícil para un lector de su código para comprender exactamente lo que va a suceder.

El error en la implementación es que 99887766555443314 es incorrecto. No acepta public static SoilTypes Default = ...5 Ni cualquier tipo definido por el usuario o un tipo estándar futuro que admite public static SoilTypes Default = ...6 y 99887766555443317 . Presumiblemente, en lugar de verificar la membresía en una lista de tipos, debe verificar si public static SoilTypes Default = ...8 99887766555443319 se definen.

 

I think there are two flaws with this function; the first is in conception, and the second is in implementation.

The conception problem is this: What does genuine_apply really do? You pass it a callable and an argument. If the argument is a tuple-like object, you expand it and pass its elements as separate arguments to the callable. If you pass it a non-tuple-like argument, you pass the argument directly to the callable. What are the circumstances in which you want that behavior? Think of apply as an aid to generic programmers; they know whether they're passing a tuple-like argument to apply because they construct the argument. Typically, you wouldn't pass user-provided arguments directly to apply, you'd construct the argument yourself from the user-provided arguments. Here, you'll get different behavior based on whether the user passes a vector or a tuple. In short, it makes it very difficult for a reader of your code to understand exactly what is going to happen.

The error in implementation is that is_tuple_like is incorrect. It does not accept std::pair nor any user-defined type or future standard type that supports std::get and std::tuple_size. Presumably instead of checking for membership in a list of types, you should check whether std::get<0>(declval(T)) and std::tuple_size<T>::value are defined.

 
 
2
 
vote

hm, ok ... pasar tiempo en los requisitos es un paso uno no puede saltar.

public TType Undefined => this[0];0 es una solución genérica a una llamada de función a la función desconocida con cero o más argumentos de "cualquier cosa". (y sí, 'Ruds' también se trató de public TType Undefined => this[0];1 no se maneja por public TType Undefined => this[0];2 ) ...

Se puede escribir algunas utilidades para ayudar a los casos de uso particulares. Como (por ejemplo,) pasando matrices, o en las listas de inicio y una tal. Lo que nuevamente resultará en transformaciones a las tuplas que no cambien de cambios del public TType Undefined => this[0];3 .

Como un boceto rápido, tal vez algo como este (no un C ++ impecable):

  public TType Undefined => this[0];4  

hth

 

Hm, ok ... spending time on requirements is a step one can not jump over.

std::apply is a generic solution to a function call to the unknown function with zero or more arguments of "anything". (and yes 'ruds' was also right about std::pair not being handled by is_tuple_like) ...

One can possibly write some utilities to aid particular use cases. Like (for example) passing arrays, or init lists and a such. Which again will result in transformations to tuples not changes of the std::apply.

As a quick sketch maybe something like this (not a flawless c++) :

  struct apply_helper final {     // static assert if F is Callable     // might go here         // apply the native array         template< typename F, typename T, size_t N>        auto operator ()          ( F invocable_, const T(&array_)[N]) ;         // apply the init list        template <typename F, typename T>        auto operator ()          ( F invocable_, std::initializer_list<T> && initlist_ ) ;          // and so on ..   } ; 

HTH

 
 

Relacionados problema

20  Cree una cadena C ++ usando el formato de estilo de impresión  ( Create a c string using printf style formatting ) 
A menudo es conveniente usar cadenas de formato C-Style 998877766554433665544336 A menudo encuentro los modificadores mucho más sencillos para usar que los...

3  Eliminación de variables genéricos con múltiples puntos de entrada  ( Generic variable elimination with multiple entry points ) 
Tengo este algoritmo, llamado eliminación variable , que se usa con bastante frecuencia en mi código -Base (biblioteca). Es un algoritmo para los gráficos de...

8  Conversión de STD :: Chrono :: Time_Point to / from std :: string  ( Converting stdchronotime point to from stdstring ) 
Considere estas funciones que permitan convertir checkOnline.sh4 a / FROM checkOnline.sh5 Con un formato Fecha de fecha ". checkOnline.sh6 con uso:...

5  Leetcode 928: Minimice Malware Spread II  ( Leetcode 928 minimize malware spread ii ) 
Estoy publicando mi código para un problema de código leetcode. Si desea revisar, por favor hágalo. ¡Gracias por su tiempo! Problema (este problema es el ...

5  Clase de registro de C ++  ( C logging class ) 
He creado una clase simple de registro de C ++. Produce varios loglas, y ajusta el color de la salida en consecuencia. Estoy interesado en cualquier consejo p...

2  "Multi" Guardia de alcance que usa la característica de Deducación de la plantilla  ( Multi scope guard using template argument deducing feature ) 
Uso de la función C ++ moderna Deducción del argumento de la plantilla para las plantillas de clase y Expresiones plegables Es posible implementar la...

5  C ++ Elija entre funciones de implementación de tiempo de ejecución y tiempo de compilación  ( C choose between runtime and compile time implementation functions ) 
Escribí una pequeña función de envoltura para elegir entre Tiempo de ejecución (STD) 99887766555443318 y una versión de ConsexPR, como continuación de la cl...

2  Fusionar ocurrencias adyacentes de elementos idénticos en la recopilación de datos  ( Merge adjacent occurrences of identical elements in data collection ) 
Hay una pequeña Ejercicio de programación para fusionar elementos idénticos adyacentes en una colección. Aquí hay dos soluciones (Pasando las pruebas de...

4  Implementar el servidor HTTP utilizando libevent  ( Implement http server using libevent ) 
#include "fmt/format.h" #include <cstdint> #include <cstdio> #include <event2/event-config.h> #include <event2/util.h> #include <evhttp.h> #include <memory> ...

3  Implementar el rango de Fibonacci  ( Implement fibonacci range ) 
Estoy implementando una gama FIBONACCI en C ++ 17 de tal manera que admite #include "fib.h" #include <iostream> int main() { for (int x : Fibonacci())...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos