네임스페이스(C)
C 전처리기기를 사용하여 C의 네임스페이스를 에뮬레이트하는 방법이 있는가?
난 이 선들을 따라 뭔가를 생각하고 있다.
#define NAMESPACE name_of_ns
some_function() {
some_other_function();
}
이는 다음 주소로 번역될 것이다.
name_of_ns_some_function() {
name_of_ns_some_other_function();
}
또 다른 대안은 당신의 모든 기능을 보유하도록 구조를 선언하고, 그리고 나서 당신의 기능을 정적으로 정의하는 것이다.그렇다면 글로벌 네임 구조의 이름 충돌만 걱정하면 될 거야.
// foo.h
#ifndef FOO_H
#define FOO_H
typedef struct {
int (* const bar)(int, char *);
void (* const baz)(void);
} namespace_struct;
extern namespace_struct const foo;
#endif // FOO_H
// foo.c
#include "foo.h"
static int my_bar(int a, char * s) { /* ... */ }
static void my_baz(void) { /* ... */ }
namespace_struct const foo = { my_bar, my_baz }
// main.c
#include <stdio.h>
#include "foo.h"
int main(void) {
foo.baz();
printf("%d", foo.bar(3, "hello"));
return 0;
}
위의 예에,my_bar
그리고my_baz
본체에서 직접 전화를 할 수 없고, 단지 통해서만 통한다.foo
.
동일한 서명으로 함수를 선언하는 네임스페이스가 여러 개 있는 경우 해당 집합에 대해 네임스페이스 구조를 표준화하고 런타임에 사용할 네임스페이스를 선택하십시오.
// goo.h
#ifndef GOO_H
#define GOO_H
#include "foo.h"
extern namespace_struct const goo;
#endif // GOO_H
// goo.c
#include "goo.h"
static int my_bar(int a, char * s) { /* ... */ }
static void my_baz(void) { /* ... */ }
namespace_struct const goo = { my_bar, my_baz };
// other_main.c
#include <stdio.h>
#include "foo.h"
#include "goo.h"
int main(int argc, char** argv) {
namespace_struct const * const xoo = (argc > 1 ? foo : goo);
xoo->baz();
printf("%d", xoo->bar(3, "hello"));
return 0;
}
의 my_bar
그리고my_baz
정적으로 정의되지만 기본 기능은 적절한 네임스페이스 구조를 통해 액세스할 수 있으므로 충돌하지 마십시오.
네임스페이스 접두사를 사용할 때, 나는 일반적으로 다음을 통해 활성화될 수 있는 단축된 이름에 매크로를 추가한다.#define NAMESPACE_SHORT_NAMES
헤더를 포함하기 전에머리글 Foobar.h는 다음과 같이 보일 수 있다.
// inclusion guard
#ifndef FOOBAR_H_
#define FOOBAR_H_
// long names
void foobar_some_func(int);
void foobar_other_func();
// short names
#ifdef FOOBAR_SHORT_NAMES
#define some_func(...) foobar_some_func(__VA_ARGS__)
#define other_func(...) foobar_other_func(__VA_ARGS__)
#endif
#endif
만약 내가 포함된 파일에 짧은 이름을 사용하고 싶다면, 나는 그렇게 할 것이다.
#define FOOBAR_SHORT_NAMES
#include "foobar.h"
나는 이것이 빈코 브르살로비치가 설명한 네임스페이스 매크로를 사용하는 것보다 더 깨끗하고 유용한 해결책이라고 생각한다.
나는 구조 기반 접근법을 두 가지 개선과 함께 사용한다.계층적 네임스페이스를 만들기 위해 하위 구조를 추가하고 네임스페이스 경로를 단순화하고 싶을 때 몇 가지 간단한 매크로를 정의한다.
푸바 도서관을 예로 들어보자.
푸바흐
#ifndef __FOOBAR_H__
#define __FOOBAR_H__
// definition of the namespace's hierarchical structure
struct _foobar_namespace {
struct {
void (*print)(char *s);
} text;
struct {
char *(*getDateString)(void);
} date;
};
// see the foobar.c file
// it must be the only one defining the FOOBAR macro
# ifndef FOOBAR
// definition of the namespace global variable
extern struct _foobar_namespace foobar;
# endif // FOOBAR
#endif // __FOOBAR_H__
푸바시
// the FOOBAR macro is needed to avoid the
// extern foobar variable declaration
#define FOOBAR
#include "foobar.h"
#include "foobar_text.h"
#include "foobar_date.h"
// creation of the namespace global variable
struct _foobar_namespace foobar = {
.text = {
.print = foobar_text__print
},
.date = {
.getDateString = foobar_date__getDateString
}
};
그런 다음 네임스페이스를 사용할 수 있다.
#include "foobar.h"
void main() {
foobar.text.print("it works");
}
그러나 그 사이에는 그다지 큰 차이가 없다.foobar_text__print()
그리고foobar.text.print()
두 더 두 번째가 더 읽기 쉽다고 생각하지만 의문이다.따라서 이러한 네임스페이스를 단순화하기 위해 일부 매크로를 정의하면 매우 유용하게 사용할 수 있다.
#include "foobar.h"
#define txt foobar.text
#define date foobar.date
void main() {
char *today = date.getDateString();
txt.print(today);
}
이러한 종류의 계층적 네임스페이스는 정의가 빠르고 이해하기 쉬우며 코드 상세도를 감소시킨다.
그냥 재미로, 여기 의 파일들이 있다.foobar.text
코드:
foobar_text.h
#ifndef __FOOBAR_TEXT_H__
#define __FOOBAR_TEXT_H__
void foobar_text__print(char *s);
#endif // __FOOBAR_TEXT_H__
foobar_text.c
#include <stdio.h>
#include "foobar_text.h"
void foobar_text__print(char *s) {
printf("%s\n", s);
}
## 연산자를 사용하면 된다.
#define FUN_NAME(namespace,name) namespace ## name
그리고 기능을 다음과 같이 선언한다.
void FUN_NAME(MyNamespace,HelloWorld)()
근데 좀 어색해 보이네.
수용된 답변과 유사한 접근법은 다음과 같다.
// inclusion guard
#ifndef FOOBAR_H_
#define FOOBAR_H_
// long names
void foobar_some_func(int);
void foobar_other_func();
// qualified names
#ifdef FOOBAR_SHORT_NAMES
extern struct _foobar {
void (*some_func)(int);
void (*other_func)();
} foobar;
#endif
#endif
이 헤더 파일은 .c 파일과 함께 제공되어야 한다.
#include "foobar.h"
struct _foobar foobar = {
foobar_some_func;
foobar_other_func;
};
기능을 사용할 때,
foobar.some_func(10);
foobar.other_func();
나는 C를 사용하여 네임스페이스나 템플릿의 장점을 얻는 방법에 대한 튜토리얼을 작성했다.
기본 네임스페이스의 경우 네임스페이스 이름에 컨벤션으로 접두사를 붙일 수 있다.
namespace MY_OBJECT {
struct HANDLE;
HANDLE *init();
void destroy(HANDLE * & h);
void do_something(HANDLE *h, ... );
}
라고 쓸 수 있다.
struct MY_OBJECT_HANDLE;
struct MY_OBJECT_HANDLE *my_object_init();
void my_object_destroy( MY_OBJECT_HANDLE * & h );
void my_object_do_something(MY_OBJECT_HANDLE *h, ... );
네임스페이스와 템플릿의 개념을 사용하는 두 번째 접근방식은 매크로 연결을 사용하고 다음을 포함하는 것이다.예를 들어, 나는
template<T> T multiply<T>( T x, T y ) { return x*y }
다음과 같은 템플릿 파일 사용
곱셈-곱셈-h
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);
곱셈-곱셈-c.
_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
return x*y;
}
우리는 이제 int_multiple을 다음과 같이 정의할 수 있다.이 예에서는 int_multiply.h/.c 파일을 생성하겠다.
int_nt.h
#ifndef _INT_MULTIPLY_H
#define _INT_MULTIPLY_H
#ifdef _multiply_
#undef _multiply_
#endif
#define _multiply_(NAME) int ## _ ## NAME
#ifdef _multiply_type_
#undef _multiply_type_
#endif
#define _multiply_type_ int
#include "multiply-template.h"
#endif
int_nt.c.
#include "int_multiply.h"
#include "multiply-template.c"
이 모든 것이 끝나면, 당신은 기능 및 헤더 파일을 가지게 될 것이다.
int int_multiply( int x, int y ) { return x * y }
나는 제공된 링크에 대해 훨씬 더 상세한 튜토리얼을 만들었다.이게 누군가에게 도움이 되기를!
도우미를 사용할 수 있음#define
매크로:
#include <stdio.h>
#define ns(x) gargantua_ ## x
struct ns(stats) {
int size;
};
int ns(get_size)(struct ns(stats) *st) {
return st->size;
}
void ns(set_size)(struct ns(stats) *st, int sz) {
st->size = sz;
}
int main(void) {
struct ns(stats) stats = {0};
ns(set_size)(&stats, 3);
printf("size=%d\n", ns(get_size)(&stats));
return 0;
}
전처리기에서 실행하면 다음과 같은 이점을 얻을 수 있다.
struct gargantua_stats {
int size;
};
int gargantua_get_size(struct gargantua_stats *st) {
return st->size;
}
void gargantua_set_size(struct gargantua_stats *st, int sz) {
st->size = sz;
}
int main(void) {
struct gargantua_stats stats = {0};
gargantua_set_size(&stats, 3);
printf("size=%d\n", gargantua_get_size(&stats));
return 0;
}
나는 다음과 같은 계책을 생각해 냈다.
(iii)
// NS_PREFIX controls the prefix of each type and function declared in this
// header, in order to avoid name collision.
#define NS_PREFIX myprefix_
// Makes a string from argument (argument is not macro-expanded).
#define stringify(arg) #arg
// Concatenation that macro-expands its arguments.
#define concat(p1, p2) _concat(p1, p2) // Macro expands the arguments.
#define _concat(p1, p2) p1 ## p2 // Do the actual concatenation.
// Append the namespace prefix to the identifier.
#define ns(iden) concat(NS_PREFIX, iden)
// header content, for instance :
void ns(my_function)(int arg1, ns(t) arg2, int arg3);
// Allow implementation files to use namespacing features, else
// hide them from the including files.
#ifndef _IMPL
#undef NS_PREFIX
#undef ns
#undef stringify
#undef concat
#undef _concat
#endif // _IMPL
(iii)
#define _IMPL
#include "header.h"
#undef __IMPL
위와 같은 접근법을 구축하여 펑크와 구조물에 결합하여 네임스페이스1과 네임스페이스2를 만드는 예를 들 수 있다. 기능을 유지하는 구조를 갖는 것에 비해 이것의 이점은 구조 유지 기능 접근법이 여러 개의 사이비 네임스페이스에 걸쳐 표준화된 구조를 필요로 한다는 것이다.항상 가능한 것은 아니다. 또는 논쟁의 여지가 없이 코드를 개선하지 못하는 많은 작업 없이. 또는 바람직한 것은 아니다.
매크로 확장 순서가 문제가 될 수 있을지는 확실치 않지만, 이는 GCC에서 작동하며 (이상과는 거리가 멀지만) 적절한 가독성을 유지하면서 필요한 코드 변경의 양을 최소화하는 것으로 보인다.
application.c:
#include <stdio.h>
#include "header1.h"
#include "header2.h"
/* use NAMESPACE1 and NAMESPACE2 macros to choose namespace */
int main() {
NAMESPACE1(mystruct) data1; // structure specific to this namespace
NAMESPACE2(mystruct) data2;
data1.n1 = '1';
data1.c = 'a';
data2.n2 = '2';
data2.c = 'a';
NAMESPACE1(print_struct)(&data1); // function specific to this namespace
NAMESPACE2(print_struct)(&data2);
}
헤더1.h
/* the below block is unnecessary, but gets rid of some compiler warnings */
#ifdef NAMESPACE_REAL
#undef NAMESPACE_REAL
#endif
/* edit the below lines to change the three occurrences of NAMESPACE1 to the desired namespace */
#define NAMESPACE1(name) NAMESPACE1 ## _ ## name
#define NAMESPACE_REAL(name) NAMESPACE1(name)
/* don't edit the next block */
#define TYPEDEF(name, ...) typedef struct NAMESPACE_REAL(name) { __VA_ARGS__ } NAMESPACE_REAL(name)
#define STRUCT(name) struct NAMESPACE_REAL(name)
#define FUNC(name) NAMESPACE_REAL(name)
/* normal header code, using FUNC and STRUCT macros */
#include <stdio.h>
TYPEDEF(mystruct,
char n1;
char c;
);
void FUNC(print_struct)(STRUCT(mystruct) *data);
/* don't edit the rest */
#undef TYPEDEF
api1.c:
#include "header1.h"
/* normal code, using FUNC and STRUCT macros */
void FUNC(print_struct)(STRUCT(mystruct) *data) {
printf("this is the struct from namespace1: %c %c\n", data->n1, data->c);
}
/* don't edit the rest */
#undef STRUCT
#undef FUNC
#undef NAMESPACE
#undef NAMESPACE_REAL
헤더2.h 및 api2.c의 다른 코드는 네임스페이스 "NAMESpace2"에 대해 수정된 헤더1.h 및 헤더2.h와 동일하다.
나는 이것이 오래된 질문(11세)이라는 것을 알고 있지만, 나는 본질적으로 당신이 위에서 열거한 바와 같이 당신이 본래 원한다고 생각하는 것을 성취하려고 노력하고 있었다.
나는 내 기능 앞에 네임스페이스가 있길 원했다.하지만 네임스페이스를 바꿀 수 있는 능력을 원했어기본적으로 이 예제에 네임스페이스가 없기를 원했지만, 이름 지정 충돌이 발생하면 라이브러리의 모든 기능에 네임스페이스를 적용할 수 있는 기능을 원했다.(기본적으로 네임스페이스가 있고 사용하는 C++에 비해 약간 뒤바뀐 것이다.)using namespace whatever
매번 네임스페이스를 지정하지 않아도 된다.)C+++++++++++++++의 cprop을 a에 떨어뜨리면 C와 마찬가지로 a a a a 이 cprop도 a에 넣는다.using namespace
문과 별칭으로 코드에 전화 코드를 업데이트해야 한다.다른 매크로 시퀀스를 작성해서 자동으로 전화 이름을 바꿀 수도 있지만, 그건 내가 생각하기에 당신이 찾던 범위 밖이야.
#include <stdio.h>
#define NAMESPACE(...) test_ //Use this as my prepender
//Where all the magic happens which could be included in a header file.
#ifndef NAMESPACE
//No Namespace by default
#define NAMESPACE(...)
#endif
//Actual replacements
#define NSPREPENDER(...) NSPROCESSING(NAMESPACE(), __VA_ARGS__)
#define NSPROCESSING(...) NSFINALIZE(__VA_ARGS__)
#define NSFINALIZE(a,b) a ## b
//BEGIN ACTUAL PROGRAM
//Prototype
void NSPREPENDER(myprint)();
int main()
{
test_myprint(); //If NAMESPACE(...) is defined to anything else, this code must change.
return 0;
}
//Implementation
void NSPREPENDER(myprint)()
{
puts("Testing");
}
이 코드는 가변 매크로를 사용하기 때문에 C99 이상에서만 컴파일할 것이다.이 매크로들은 재귀의 형태를 하는데, 이것은 모두 우리가 맨 위에 정의한 매크로에서 값을 잡을 수 있도록 하기 위해서 행해진다.
모든 기능 분석:
- 우리는 우리의 네임스페이스를 원한다고 정의한다.
- 정의된 항목이 없는 경우 기본값 설정
- 여러 번 전화를 걸어 전처리기 기능을 바이패스 및 (ab)사용하십시오.
- 각 c 함수에 NSPREPENDER 매크로 함수를 추가하여 이름을 더럽힐 수 있도록 한다.
- 컴파일러가 볼 때쯤이면 이름이 제대로 망가지기 때문에 망가지 이름을 사용하여 코드를 작성한다.
이 코드는 clang으로 테스트되었다.
참조URL: https://stackoverflow.com/questions/389827/namespaces-in-c
'Programing' 카테고리의 다른 글
vuex 저장소 형식에서 개체 속성을 다이너믹하게 설정하는 방법(유형 안전 유지 중) (0) | 2022.05.03 |
---|---|
vuex 입력을 편집하기 위해 vuex 개체를 바인딩하는 깨끗한 방법 (0) | 2022.05.03 |
META-INF의 목적은 무엇인가? (0) | 2022.05.02 |
iOS는 기가바이트의 데이터를 저장할 때 진행형 웹 앱을 제한하는가? (0) | 2022.05.02 |
malloc는 gcc에서 값을 0으로 초기화하는 이유는? (0) | 2022.05.02 |