Así que estoy trabajando en un diseño de software usando C para un procesador determinado. El kit de herramientas incluye la capacidad de compilar C y C ++. Por lo que estoy haciendo, no hay asignación de memoria dinámica disponible en este entorno y el programa en general es bastante simple. Sin mencionar que el dispositivo casi no tiene potencia de procesador o recursos. Realmente no hay una gran necesidad de usar ningún C ++.
Dicho esto, hay algunos lugares donde realizo sobrecargas de funciones (una característica de C ++). Necesito enviar algunos tipos diferentes de datos y no tengo ganas de usar el printf
formato de estilo con algún tipo de %s
argumento (o lo que sea). He visto a algunas personas que no tenían acceso a un compilador de C ++ haciendo la printf
cosa, pero en mi caso el soporte de C ++ está disponible.
Ahora estoy seguro de que podría tener la pregunta de por qué necesito sobrecargar una función para empezar. Así que intentaré responder eso ahora. Necesito transmitir diferentes tipos de datos a un puerto serie, así que tengo algunas sobrecargas que transmiten los siguientes tipos de datos:
unsigned char*
const char*
unsigned char
const char
Prefiero no tener un método que maneje todas estas cosas. Cuando llamo a la función, solo quiero que transmita el puerto serie, no tengo muchos recursos, así que no quiero hacer nada más que mi transmisión.
Alguien más vio mi programa y me preguntó, "¿por qué estás usando archivos CPP?" Entonces, esa es mi única razón. ¿Esa es una mala práctica?
Actualizar
Me gustaría abordar algunas preguntas:
Una respuesta objetiva a su dilema dependerá de:
Si el tamaño del ejecutable crece significativamente si usa C ++.
En este momento, el tamaño del ejecutable consume el 4.0% de la memoria del programa (de 5248 bytes) y el 8.3% de la memoria de datos (de 342 bytes). Es decir, compilar para C ++ ... No sé cómo sería para C porque no he estado usando el compilador de C. Sé que este programa no crecerá más, así que por lo limitados que son los recursos, diría que estoy bien allí ...
Si hay algún impacto negativo notable en el rendimiento si usa C ++.
Bueno, si lo hay, no me he dado cuenta de nada ... pero, de nuevo, esa podría ser la razón por la que hago esta pregunta, ya que no entiendo completamente.
Si el código se puede reutilizar en una plataforma diferente donde solo está disponible un compilador de C.
Sé que la respuesta a esto es definitivamente no . De hecho, estamos considerando cambiarnos a un procesador diferente, pero solo a procesadores basados en ARM más potentes (todo lo cual sé con certeza que tienen cadenas de herramientas de compilación C ++).
//
comentarios. Si funciona, ¿por qué no?//
comentarios han estado en el estándar C desde C99.Respuestas:
No iría tan lejos como para llamarlo "mala práctica" per se , pero tampoco estoy convencido de que realmente sea la solución correcta a su problema. Si todo lo que quiere son cuatro funciones separadas para hacer sus cuatro tipos de datos, ¿por qué no hacer lo que los programadores de C han hecho desde tiempos inmemoriales?
Eso es efectivamente lo que el compilador de C ++ está haciendo entre bastidores de todos modos y no es una gran carga para el programador. Evita todos los problemas de "por qué estás escribiendo no completamente C con un compilador de C ++", y significa que nadie más en tu proyecto se va a confundir por qué bits de C ++ están "permitidos" y cuáles no.
fuente
#define
transmitir () usando C11_Generic
Usar solo algunas características de C ++ y tratarlo como C no es exactamente común, pero tampoco es desconocido. De hecho, algunas personas incluso no usan ninguna función de C ++, excepto la verificación de tipos más estricta y poderosa. Simplemente escriben C (teniendo cuidado de escribir solo en la intersección común de C ++ y C), luego compilan con un compilador de C ++ para la verificación de tipos y un compilador de C para la generación de código (o simplemente se quedan con un compilador de C ++).
Linux es un ejemplo de una base de código, donde la gente se pregunta regularmente que los identificadores como
class
se renombran aklass
, okclass
, de modo que puedan compilar Linux con un compilador de C ++. Obviamente, dada la opinión de Linus sobre C ++ , siempre son derribados :-D GCC es un ejemplo de una base de código que primero se transformó en "C ++ - clean C", y luego se refactorizó gradualmente para usar más funciones de C ++.No hay nada malo con lo que estás haciendo. Si está realmente paranoico acerca de la calidad de generación de código de su compilador de C ++, puede usar un compilador como Comeau C ++, que compila a C como lenguaje de destino, y luego usa el compilador de C. De esa manera, también puede hacer inspecciones puntuales del código para ver si el uso de C ++ inyecta algún código imprevisto sensible al rendimiento. Sin embargo, ese no debería ser el caso para la sobrecarga, que literalmente genera automáticamente funciones con nombres diferentes: IOW, exactamente lo que harías en C de todos modos.
fuente
Una respuesta objetiva a su dilema dependerá de:
Si las respuestas a alguna de las preguntas son "sí", es mejor que cree funciones con nombres diferentes para diferentes tipos de datos y se quede con C.
Si las respuestas a todas las preguntas son "no", no veo ninguna razón por la que no deba usar C ++.
fuente
Planteas la pregunta como si compilar con C ++ simplemente te diera acceso a más funciones. Este no es el caso. Compilar con un compilador de C ++ significa que su código fuente se interpreta como fuente de C ++, y C ++ es un lenguaje diferente de C. Los dos tienen un subconjunto común lo suficientemente grande como para programar de manera útil, pero cada uno tiene características que el otro carece, y es posible escribir código que sea aceptable en ambos idiomas pero interpretado de manera diferente.
Si realmente todo lo que desea es la sobrecarga de funciones, entonces realmente no veo el punto de confundir el problema al incorporar C ++. En lugar de diferentes funciones con el mismo nombre, distingue sus listas de parámetros, simplemente escriba funciones con diferentes nombres.
En cuanto a sus criterios objetivos,
El ejecutable puede ser ligeramente más grande cuando se compila como C ++, en relación con un código C similar creado como tal. Como mínimo, el ejecutable de C ++ tendrá el dudoso beneficio de la manipulación de nombres de C ++ para todos los nombres de funciones, en la medida en que los símbolos se conserven en el ejecutable. (Y de hecho, eso es lo que proporciona sus sobrecargas en primer lugar). Si la diferencia es lo suficientemente grande como para ser importante para usted es algo que tendría que determinar mediante un experimento.
Dudo que vea una diferencia de rendimiento notable para el código que describe frente a un hipotético análogo puro de C.
Yo arrojaría una luz ligeramente diferente sobre esto: si desea vincular el código que está creando con C ++ a otro código, entonces ese otro código también deberá escribirse en C ++ y construirse con C ++, o necesitará para hacer una provisión especial en su propio código (declarando el enlace "C"), que, además, no puede hacer para sus funciones sobrecargadas. Por otro lado, si su código está escrito en C y compilado como C, entonces otros pueden vincularlo con los módulos C y C ++. Este tipo de problema generalmente se puede superar cambiando las tornas, pero dado que realmente no parece necesitar C ++, ¿por qué aceptar ese problema en primer lugar?
fuente
En el pasado, se promovió el uso de un compilador de C ++ como "una mejor C" como un caso de uso. De hecho, principios de C ++ fue exactamente eso. El principio de diseño subyacente era que solo podía usar las funciones que deseaba y no incurriría en el costo de las funciones que no estaba usando. Por lo tanto, podría sobrecargar las funciones (¡declarando la intención a través de la
overload
palabra clave!) Y el resto de su proyecto no solo se compilaría bien, sino que generaría un código no peor de lo que produciría el compilador de C.Desde entonces, los idiomas han divergido un poco.
Todo
malloc
en su código C será un error de desajuste de tipo, ¡no es un problema en su caso sin memoria dinámica! Del mismo modo, todos susvoid
punteros en C lo harán tropezar, ya que tendrá que agregar lanzamientos explícitos. Pero ... ¿por qué lo haces de esa manera? ... te guiarán por el uso de las funciones de C ++ cada vez más.Por lo tanto, podría ser posible con algo de trabajo extra. Pero servirá como una puerta de entrada a la adopción de C ++ a mayor escala. En unos años, la gente se quejará de su código heredado que parece que fue escrito en 1989, ya que reemplazan sus
malloc
llamadas connew
, extraen bloques de código de cuerpos de bucle para llamar a un algoritmo en su lugar y reprenden el polimorfismo falso inseguro que podría han sido triviales si se hubiera permitido al compilador hacerlo.Por otro lado, sabes que sería lo mismo si lo hubieras escrito en C, así que ¿alguna vez es incorrecto escribir en C en lugar de C ++ dada su existencia? Si la respuesta es "no", tampoco puede estar equivocado el uso de funciones seleccionadas de C ++ .
fuente
Muchos lenguajes de programación tienen una o más culturas que se desarrollan a su alrededor y tienen ideas particulares sobre lo que se supone que es un lenguaje. Si bien debería ser posible, práctico y útil tener un lenguaje de bajo nivel adecuado para la programación de sistemas que aumente un dialecto de C adecuado para tal propósito con algunas características de C ++ que también serían adecuadas, las culturas que rodean los dos idiomas no tienen Particularmente favorecen tal fusión.
He leído sobre compiladores C integrados que incorporaron algunas características de C ++, incluida la capacidad de tener
interpretado como, esencialmente
así como la capacidad de sobrecargar funciones estáticas (los problemas de cambio de nombre solo surgen cuando se exportanlas funciones están sobrecargadas). No hay ninguna razón por la que los compiladores de C no puedan soportar tal funcionalidad sin imponer requisitos adicionales en el entorno de enlace y tiempo de ejecución, pero el rechazo reflexivo de muchos compiladores de C de casi todo desde C ++ con el fin de evitar las cargas ambientales les hace rechazar incluso características que no impondrían tal costo. Mientras tanto, la cultura de C ++ tiene poco interés en cualquier entorno que no sea compatible con todas las características de ese lenguaje. A menos que o hasta que la cultura de C cambie para aceptar tales características, o la cultura de C ++ cambie para alentar la implementación de subconjuntos livianos, el código "híbrido" puede sufrir muchas de las limitaciones de ambos lenguajes, al tiempo que está limitado en su capacidad para cosechar Los beneficios de operar en un superconjunto combinado.
Si una plataforma admite C y C ++, el código de biblioteca adicional requerido para admitir C ++ probablemente hará que el ejecutable sea algo más grande, aunque el efecto no será particularmente grande. La ejecución de "características C" dentro de C ++ probablemente no se verá particularmente afectada. Un problema mayor puede ser cómo los optimizadores C y C ++ manejan varios casos de esquina. El comportamiento de los tipos de datos en una C se define principalmente por el contenido de los bits almacenados en él, y C ++ tiene una categoría reconocida de estructuras (PODS - Estructuras de datos antiguas y simples) cuya semántica también se define principalmente por los bits almacenados. Sin embargo, los estándares C y C ++ a veces permiten un comportamiento contrario al que implican los bits almacenados, y las excepciones al comportamiento de "bits almacenados" difieren entre los dos lenguajes.
fuente
Otro factor importante a considerar: quien herede su código.
¿Esa persona siempre será un programador de C con un compilador de C? Supongo que sí.
¿Esa persona también será un programador de C ++ con un compilador de C ++? Me gustaría estar razonablemente seguro de esto antes de hacer que mi código dependa de cosas específicas de C ++.
fuente
El polimorfismo es una característica realmente buena que C ++ proporciona de forma gratuita. Sin embargo, hay otros aspectos que debe tener en cuenta al elegir un compilador. Hay una alternativa para el polimorfismo en C, pero su uso puede causar algunas cejas levantadas. Es la función variadic, consulte el tutorial de la función Variadic .
En tu caso sería algo como
Me gusta el enfoque de Phillips, pero llena su biblioteca con muchas llamadas. Con lo anterior, la interfaz está limpia. Tiene sus inconvenientes y, en última instancia, es una cuestión de elección.
fuente
Arg_type_t
y unvoid *
apunte a los datos. Dicho esto, sí, dar a la función (única) un argumento que indique que el tipo de datos es una alternativa viable.Para su caso particular de sobrecarga estática de una "función" para algunos tipos diferentes, puede considerar usar C11 con su maquinaria de
_Generic
macro . Siento que podría ser suficiente para sus necesidades limitadas.Usando la respuesta de Philip Kendall, podría definir:
y codifique
transmit(foo)
cualquiera que sea el tipo defoo
(entre los cuatro tipos enumerados anteriormente).Si solo le interesan los compiladores GCC (y compatibles, por ejemplo, Clang ), podría considerar
__builtin_types_compatible_p
sutypeof
extensión.fuente
En mi humilde opinión, sí, y tendré que volverme esquizofrénico para responder a esto ya que amo ambos idiomas, pero no tiene nada que ver con la eficiencia, sino más bien con la seguridad y el uso idiomático de los idiomas.
Lado C
Desde el punto de vista de C, considero que es un desperdicio hacer que su código requiera C ++ solo para usar la sobrecarga de funciones. A menos que lo esté utilizando para el polimorfismo estático con plantillas de C ++, es un azúcar sintáctico tan trivial obtenido a cambio de cambiar a un lenguaje completamente diferente. Además, si alguna vez desea exportar sus funciones a un dylib (puede o no ser una preocupación práctica), ya no puede hacerlo de manera muy práctica para un consumo generalizado con todos los símbolos de nombre mutilado.
Lado C ++
Desde el punto de vista de C ++, no debería usar C ++ como C con sobrecarga de funciones. Este no es un dogmatismo estilístico, sino uno relacionado con el uso práctico de C ++ cotidiano.
Su tipo normal de código C es razonablemente sensato y "seguro" para escribir si está trabajando en contra del sistema de tipo C que prohíbe cosas como los copiadores
structs
. Una vez que esté trabajando en el sistema de tipos mucho más rico de C ++, las funciones diarias que son de gran valor comomemset
ymemcpy
no se convierten en funciones en las que debe apoyarse todo el tiempo. En cambio, son funciones que generalmente desea evitar como la peste, ya que con los tipos C ++, no debería tratarlos como bits y bytes sin procesar para copiar, barajar y liberar. Incluso si su código solo usa cosas comomemset
primitivas y UDT de POD en este momento, en el momento en que alguien agrega un ctor a cualquier UDT que usa (incluso solo agrega un miembro que requiere uno, comostd::unique_ptr
miembro) contra tales funciones o una función virtual o algo por el estilo, hace que toda su codificación normal de estilo C sea susceptible a un comportamiento indefinido. Tómelo del propio Herb Sutter:Muchos desarrolladores de C no estarían de acuerdo con esto y con razón, ya que la filosofía solo se aplica si está escribiendo código en C ++. Lo más probable está escribiendo código muy problemático si se utilizan funciones como
memcpy
todos los tiempos en el código que construye como C ++ , pero es perfectamente bien si lo hace en C . Los dos idiomas son muy diferentes a este respecto debido a las diferencias en el sistema de tipos. Es muy tentador observar el subconjunto de características que estos dos tienen en común y creer que uno puede usarse como el otro, especialmente en el lado de C ++, pero el código C + (o código C--) es generalmente mucho más problemático que C y C Código C ++.Del mismo modo, no debería usar, digamos,
malloc
en un contexto de estilo C (lo que implica que no hay EH) si puede llamar a cualquier función C ++ directamente que pueda lanzar, ya que tiene un punto de salida implícito en su función como resultado de excepción que no puede atrapar efectivamente escribiendo código de estilo C, antes de poder acceder afree
esa memoria. Así que cuando usted tiene un archivo que se acumula como C ++ con una.cpp
extensión o lo que sea y lo hace todo este tipo de cosas comomalloc
,memcpy
,memset
,qsort
, etc., entonces está pidiendo problemas más adelante si no es así, a menos que sea el detalle de implementación de una clase que solo funciona con tipos primitivos, momento en el que todavía necesita hacer un manejo de excepciones para estar seguro de excepciones. Si estás escribiendo código C ++ por el contrario quiere generalmente se basan en RAII y usar cosas comovector
,unique_ptr
,shared_ptr
, etc, y evitar todos normales de codificación de estilo C, cuando sea posible.Excepción-Seguridad
Nuevamente, en el momento en que esté en C ++ land, la gente esperará que su código sea seguro para las excepciones. Las personas pueden mantener su código en el futuro, dado que ya está escrito y compilado en C ++, y simplemente usar
std::vector, dynamic_cast, unique_ptr, shared_ptr
, etc., en código llamado directa o indirectamente por su código, creyendo que es inocuo ya que su código ya es "supuestamente" C ++ código. En ese punto, tenemos que enfrentar la posibilidad de que las cosas salgan mal, y luego, cuando tomas un código C perfecto y encantador como este:... ahora está roto. Necesita ser reescrito para ser seguro para excepciones:
¡Bruto! Es por eso que la mayoría de los desarrolladores de C ++ exigirían esto en su lugar:
Lo anterior es un código seguro de excepción conforme a RAII del tipo que los desarrolladores de C ++ generalmente aprobarían, ya que la función no perderá ninguna línea de código que desencadene una salida implícita como resultado de a
throw
.Elige un idioma
Debe adoptar el sistema de tipos y la filosofía de C ++ con RAII, excepción de seguridad, plantillas, OOP, etc., o adoptar C, que gira en gran medida en torno a bits y bytes sin formato. No debe formar un matrimonio impío entre estos dos idiomas, y en su lugar separarlos en idiomas distintos para que sean tratados de manera muy diferente en lugar de difuminarlos.
Estos idiomas quieren casarse contigo. Por lo general, debes elegir uno en lugar de salir y perder el tiempo con ambos. O puedes ser un polígamo como yo y casarte con ambos, pero debes cambiar completamente tu forma de pensar cuando pasas tiempo uno con el otro y mantenerlos bien separados entre sí para que no peleen entre ellos.
Tamaño binario
Solo por curiosidad, traté de tomar mi implementación de lista gratuita y punto de referencia en este momento y portarlo a C ++ ya que tenía mucha curiosidad sobre esto:
... y quería saber si el tamaño binario se inflaría simplemente construyéndose como C ++. Me obligó a esparcir lanzamientos explícitos por todo el lugar, lo cual era fugitivo (una razón por la que me gusta escribir cosas de bajo nivel como asignadores y estructuras de datos en C mejor) pero solo me llevó un minuto.
Esto era solo comparar una compilación de lanzamiento MSVC de 64 bits para una aplicación de consola simple y con un código que no usaba ninguna función de C ++, ni siquiera la sobrecarga del operador, solo la diferencia entre construirlo con C y usar, por ejemplo, en
<cstdlib>
lugar de<stdlib.h>
y cosas así, pero me sorprendió descubrir que no hizo ninguna diferencia en el tamaño binario.El binario era
9,728
bytes cuando se construyó en C, y también9,278
bytes cuando se compiló como código C ++. En realidad no esperaba eso. Pensé que cosas como EH al menos agregarían un poco allí (pensé que al menos serían como cien bytes diferentes), aunque probablemente fue capaz de darse cuenta de que no había necesidad de agregar instrucciones relacionadas con EH ya que solo estoy usando la biblioteca estándar C y nada arroja. Pensé algoagregaría un poco al tamaño binario de cualquier manera, como RTTI. De todos modos, fue genial ver eso. Por supuesto, no creo que debas generalizar a partir de este resultado, pero al menos me impresionó un poco. Tampoco tuvo ningún impacto en los puntos de referencia, y naturalmente, ya que me imagino que el tamaño binario resultante idéntico también significó instrucciones de máquina idénticas.Dicho esto, ¿a quién le importa el tamaño binario con los problemas de seguridad e ingeniería mencionados anteriormente? Así que de nuevo, elige un idioma y adopta su filosofía en lugar de tratar de bastardarlo; Eso es lo que recomiendo.
fuente