paises mundo mas espacial contaminantes basura c++ c++11 header namespaces wrapper

c++ - mundo - ¿Cómo no contaminar el espacio de nombres global con declaraciones de un encabezado C?



paises mas contaminantes del mundo 2017 (3)

No estoy seguro de si el lenguaje es legal, pero creo que la extern "C" solo está ahí para desentrañar las funciones, de modo que mientras las mantenga en el archivo .cpp puede salirse con la suya.

Esto es un poco profano, pero parece funcionar con gcc 4.3.5. Demuestra que puede usar funciones C al mismo tiempo que las esconde en un espacio de nombres.

No me molesté en heredar struct_t, pero probablemente debería funcionar. No tengo idea si puedes llevar a cabo la enum class .

foo.h

#ifndef foo_H #define foo_H typedef enum { ALPHA, BETA } enum_t; typedef struct { int i; } struct_t; void printit(struct_t print_me); #endif // foo_H

foo.c

#include <stdio.h> #include "foo.h" void printit (struct_t print_me) { printf ("Hello World %d!/n", print_me.i); }

bar.hpp

#ifndef bar_HPP #define bar_HPP namespace _foo { // Don''t need extern "C" since we''re not using functions #include "foo.h" } struct based_on_struct_t // : public _foo:struct_t // Do you really have to derive? It might be possible, but it''s ugly { _foo::struct_t i; double j; based_on_struct_t (int _i, double _j) : j(_j) { i.i = _i; } void print(void); // Gonna call printit, MUST be in .cpp }; #endif // bar_HPP

bar.cpp

namespace _foo{ extern "C" { #include "foo.h" } } #include "bar.hpp" #include <stdio.h> void based_on_struct_t::print (void) { // Call the old version... printit(i); // And do new crap printf ("Goodbye World %d %f/n", i.i, j); }

driver.cpp

#include "bar.hpp" int main (void) { based_on_struct_t B(10, .1); B.print(); return 0; }

Manifestación...

$ gcc foo.c -c -O3 $ g++ foo.o bar.cpp driver.cpp $ ./a.out Hello World 10! Goodbye World 10 0.100000 $

Estoy tratando de envolver una biblioteca de C en C ++, para convertirla en una biblioteca de C ++ moderna, de alto nivel e idiomática. Lo que quiero hacer es hacer que los objetos C sean completamente opacos y / o directamente no disponibles desde el código C ++ y envolverlos / reemplazarlos con alternativas de mayor nivel.

El problema al que me enfrento es simple: quiero incluir el encabezado C solo en la fuente C ++, de modo que el encabezado C ++ cuando esté incluido no incluya también las declaraciones del encabezado C, es decir, no contaminará el espacio de nombres global.

Pero parece que la separación correcta del encabezado y los archivos fuente no me permite hacer eso. Aquí hay una versión muy simplificada de mi problema, los comentarios le dirán el resto:

my_header.h:

typedef enum { my_Consts_ALPHA = /* some special value */, my_Consts_BETA = /* other special value */, } my_Consts; typedef struct { // members... } my_Type; void my_Type_method(my_Type *const, my_Enum);

my_header.hpp:

namespace my { enum class Consts; // <-- This header is missing the constant values of // this enum, because its values are defined by // the C header :( class Type : public my_Type // <-- The super struct is coming from the // C header, but I don''t want to include // that header here :( { public: void method(Consts constant); }; }

my_source.cpp:

extern "C" { #include "my_header.h" } #include "my_header.hpp" namespace my { enum class Consts { ALPHA = my_Consts_ALPHA, BETA = my_Consts_BETA, }; void Type::method(Consts constant) { my_Type_method(static_cast<my_Type *const>(this), static_cast<my_Consts>(constant)); } }

Entonces mis preguntas son: ¿Me estoy perdiendo algo muy obvio aquí? ¿Es esto posible de lograr? ¿Hay algún truco del que no tenga conocimiento?


Si la idea de escribir envoltorios C ++ de alto nivel e idiomáticos es brindar seguridad, administración automática de la memoria y tipos convenientes de C ++ como std::sting , incluiría el encabezado C solo en el archivo cpp .

Proporcione una interfaz idiomática C ++ limpia y use la biblioteca C solo en la implementación.

No tema escribir un par de funciones de utilidad que conviertan datos C a C ++ y viceversa. Si una clase de C ++ debe contener datos específicos de C, y no es posible reemplazarla con un análogo de C ++, use alguna técnica de borrado de tipo para mantener la interfaz limpia.

No me preocuparía el rendimiento debido a dicho ajuste hasta que lo vea en la parte superior de un registro de perfilador. En la mayoría de los casos, no es un cuello de botella.

Una vez más, dividir la interfaz y la implementación suele ser una victoria.

ACTUALIZAR

Inicialmente, estaba pensando más en la interfaz de C ++ específica para el proyecto que en la envoltura universal de C ++ en la biblioteca C.

La solución con extern "C" envuelta en un espacio de nombres me parece correcta (ver §7.5 del estándar C ++ 11). Pero, nunca he visto esta técnica en la naturaleza.

Puede ir más allá y agregar espacio de nombres de detail anidados para no contaminar my espacio de nombres con tipos de C. Este truco es popular en las bibliotecas de solo encabezado:

namespace my { namespace detail { extern "C" { #include "my_header.h" } } enum class Consts { ALPHA = detail::my_Consts_ALPHA, BETA = detail::my_Consts_BETA, }; class Type : public detail::my_Type { public: void method(Consts constant); }; }

Tenga en cuenta que no puede hacer que las funciones de C sean completamente opacas o envolverlas en un solo espacio de nombres cuando se vincula con la biblioteca estática. Tienen enlaces externos y no saben nada sobre espacios de nombres.

namespace A { extern "C" void my_Type_method(my_Type *const, my_Enum); } namespace B { extern "C" void my_Type_method(my_Type *const, my_Enum); } extern "C" void my_Type_method(my_Type *const, my_Enum);

Básicamente, todas estas declaraciones se refieren a la misma función C. Como C no admite espacios de nombres ni sobrecargas, el vinculador usualmente usa nombres de funciones como identificadores únicos (incluso los tipos de argumentos son ignorados).

De todos modos, este enfoque ayudará a evitar el acceso accidental a la interfaz C.


En los comentarios de la pregunta @AnalPhabet sugirió sarcásticamente, ese debería usar #include de un encabezado C dentro de un namespace . @nm confirmó, que en realidad es una solución de trabajo, y ahora lo probé en mi propia configuración, y afortunadamente está funcionando bastante bien.

(Aunque no tengo ni idea, si esto es específico de la implementación o no, pero probé tanto en g++ como en clang++ y está funcionando).

No resuelve el problema de opacidad , pero al menos dificulta un poco el acceso a los datos brutos de C directamente ya que ahora vive en un namespace separado, por lo tanto, el usuario no puede acceder accidentalmente, sino de buena gana.

Por lo tanto, el my_header.hpp debería verse así:

namespace my { extern "C" { #include "my_header.h" } enum class Consts { ALPHA = my_Consts_ALPHA, BETA = my_Consts_BETA, }; class Type : public my_Type { public: void method(Consts constant); }; }

Entonces, donde sea que my_header.hpp sea #include ''d, el usuario solo puede acceder a los valores C de la siguiente manera:

my::my_Consts_ALPHA // The wrapped value is => my::Consts::ALPHA my::my_Type // The wrapped value is => my::Type my::my_Type_method(t,..) // The wrapped value is => t.method(..)