2009-03-09 5 views
8

VB6 및 FoxPro 같은 여러 프로그래밍 환경을 지원하는 라이브러리에서 작업하고 있습니다. 가장 일반적인 공통 분모이므로 C 규칙을 고수해야합니다. 이제 스타일에 관한 질문이 있습니다.C API 함수 스타일

함수 프로세스가 문자열을 입력하고 반환한다고 가정합니다. 이 과정에서 오류가 발생할 수 있습니다. 현재 제안 된 스타일은 다음과 같습니다.

int func(input params... char* buffer, unsigned int* buffer_size); 

이 스타일의 좋은 점은 오류 코드를 비롯하여 모든 것이 프로토 타입에 포함되어 있다는 것입니다. 그리고 메모리 할당을 피할 수 있습니다. 문제는 그 기능이 아주 장황하다는 것이다. 그리고 buffer_size는 any 일 수 있기 때문에 더 많은 코드를 구현해야합니다.

char* func(input params...); 

이 스타일은 버퍼를 삭제하는 호출자가 필요합니다

또 다른 옵션은 숯불 *를 반환하고, 오류를 나타 내기 위해 NULL을 반환하는 것입니다. 서버 프로그램이 메모리 조각화 문제를 겪을 수 있으려면 메모리 할당이 필요합니다.

두 번째 옵션의 변형은 스레드 로컬 변수를 사용하여 반환 된 포인터 char *를 보유하여 사용자가 버퍼를 삭제할 필요가 없도록하는 것입니다.

어떤 스타일을 좋아합니까? 그리고 이유는?

+0

버퍼 유형이 char **가 아니어야합니까? 또한, 왜 옵션 1에 buffer_size가 필요하고 옵션 2에는 buffer_size가 필요하지 않습니까? – mweerden

+0

그는 미리 할당 된 버퍼를 in 매개 변수로 전달하고 호출 된 함수가 오류 텍스트로 채울 것으로 기대합니다. – sharptooth

+0

그래, 그렇지만 buffer_size가 포인터 일 필요는 없지, 그렇지? – mweerden

답변

1

두 번째 변형은 더 깨끗합니다.

COM IErrorInfo는 두 번째 방법을 구현 한 것입니다. 서버는 SetErrorInfo를 호출하여 잘못된 정보를 설정하고 오류 코드를 반환합니다. 호출자는 코드를 검사하고 GetErrorInfo를 호출하여 세부 정보를 가져올 수 있습니다. 호출자는 IErrorInfo를 해제해야하지만 첫 번째 변형에서 각 호출의 매개 변수를 전달하는 것은 아름답 지 않습니다.

서버는 시작할 때 충분한 메모리를 미리 할당 할 수 있으므로 오류 세부 정보를 반환 할만큼 충분한 메모리가 있어야합니다.

2

두 가지 스타일 중에서 선택해야한다면 매번 1 번째 스타일을 선택해야합니다. 두 번째 스타일은 도서관 사용자가 생각할 수있는 다른 것, 기억 할당 (memeory allocation)을 제공하며, 누군가는 기억을 해방하는 것을 잊어 버릴 수밖에 없습니다.

5

버퍼와 크기가 전달되는 첫 번째 정의를 선호합니다. 예외가 있지만 일반적으로 호출 한 함수를 정리해야 할 필요는 없습니다. 반면에 메모리를 할당하고 함수에 전달하면 나 자신을 정리해야한다는 것을 알게됩니다.

다른 크기의 버퍼를 다루는 것이 중요하지 않습니다.

+0

이것은 주로 Windows API 자체에서 수행하는 작업이므로이를 에뮬레이트하는 것이 좋습니다. –

2

두 번째 스타일의 또 다른 문제는 메모리가 할당 된 컨텍스트가 다를 수 있다는 것입니다. 예 :

// your library in C 
char * foo() { 
    return malloc(100); 
} 

// my client code C++ 
char * p = foo();  // call your code 
delete p;    // natural for me to do, but ... aaargh! 

그리고이 부분은 문제의 일부에 지나지 않습니다. 양측이 malloc &을 무료로 사용해야한다고 말할 수는 있지만, 다른 컴파일러 구현을 사용한다면 어떨까요? 모든 할당과 할당 해제가 같은 위치에서 발생하는 것이 좋습니다. 이 라이브러리가 클라이언트 코드인지 여부는 귀하에게 달려 있습니다.

1

첫 번째 버전은 다른 프로그래머가 사용하면 오류가 발생하지 않을 것입니다.

프로그래머가 메모리를 직접 할당해야하는 경우 메모리를 확보 할 가능성이 더 높습니다. 라이브러리가 메모리를 할당하면 아직 또 다른 추상화가되어 있고/합병증을 유발할 수 있습니다.

1

상상할 몇 가지 사항;

  • 할당 및 할당 해제는 동일한 범위 (이상적)에서 이루어져야합니다. 호출자가 미리 할당 한 버퍼를 전달하는 것이 가장 좋습니다. 호출자는 안전하게 나중에 이것을 해제 할 수 있습니다. 이것은 버퍼가 얼마나 커야 하는가라는 질문을 던집니다. Win32에서 꽤 널리 사용되는 접근법은 NULL을 입력 버퍼로 전달하는 것이며, size 매개 변수를 사용하면 필요한 정도를 알 수 있습니다.

  • 몇 가지 가능한 오류 조건을 감독합니까? char*을 반환하면 오류보고 범위가 제한 될 수 있습니다.

  • 어떤 전후 조건을 충족 시키시겠습니까? 귀하의 프로토 타입에 반영되어 있습니까?

  • 발신자 또는 수신자의 오류를 확인합니까?

나는 큰 그림이 없기 때문에 나는 하나가 다른 것보다 낫다고 말할 수 없다. 그러나 나는 이러한 것들이 당신의 생각뿐만 아니라 다른 게시물을 얻을 수 있다고 확신합니다.

8

나는이 주제에 관해서 조금 "손상된 제품"이다. 필자는 임베디드 텔레콤 용으로 상당히 큰 API를 설계하고 유지 관리하는 데 익숙했습니다. 당신이 당연하게 여길 수없는 상황. 전역 변수 나 TLS와 같은 것은 아닙니다. 때로는 실제로 힙 (heap) 버퍼가 나타나서 실제로는 ROM 메모리로 처리됩니다.

따라서 "가장 낮은 공통 분모"를 찾고 있다면 대상 환경에서 사용할 수있는 언어 구문을 생각할 수도 있습니다 (컴파일러는 표준 C 내에서 어떤 것도 허용 할 가능성이 있지만 링커가 아니오라고 말하면 지원되지 않습니다).

그런 말로하면, 나는 항상 대안 1에 갈 것입니다. 부분적으로 (다른 사람들이 지적했듯이) 직접적으로 사용자를위한 메모리를 할당해서는 안됩니다 (간접적 인 접근법은 더 자세히 설명합니다). 사용자가 순수하고 평이한 C로 작업 할 수 있다고하더라도 누출, 진단 로깅 등을 추적하기 위해 자체적으로 사용자 정의 된 메모리 관리 API를 사용할 수 있습니다. 그런 전략에 대한 지원은 일반적으로 인정됩니다.

오류 통신은 API를 처리 할 때 가장 중요한 사항 중 하나입니다. 사용자가 자신의 코드에서 오류를 처리 할 수있는 별개의 방법을 가지고 있으므로 API 전체에서이 통신에 대해 가능한 한 일관성을 유지해야합니다. 사용자는 일관된 방법으로 최소 코드로 API에 대한 오류 처리를 래핑 할 수 있어야합니다. 일반적으로 항상 명확한 enum 코드를 사용하거나/typedef를 정의하는 것이 좋습니다. 나는 개인적으로 타입 정의를 선호 : 에드 열거 :

typedef enum { 

    RESULT_ONE, 
    RESULT_TWO 

} RESULT; 

를가/할당 보증을 제공 였으니.

get-last-error 기능을 사용하는 것도 좋지만 (중앙 저장 장치가 필요함) 개인적으로 이미 인식 된 오류에 대한 추가 정보를 제공하기 위해 개인적으로 사용합니다.더 나은

struct Buffer 
{ 
    unsigned long size; 
    char* data; 
}; 

그런 다음 API를 볼 수 있습니다 :

ERROR_CODE func(params... , Buffer* outBuffer); 

이 전략은 또한 더 정교한 위해 열어

상세도 대안 1의이 같은 간단한 화합물을 제한 할 수있다 메커니즘.

struct Buffer 
{ 
    unsigned long size; 
    char* data; 
    void* (*allocator_callback)(unsigned long size); 
    void (*free_callback)(void* p); 
}; 

당연히, 이러한 구조의 스타일은 항상 : (당신이 버퍼의 크기를 조정해야하는 경우 예) 당신이, 당신이에 간접적 인 접근 방식을 제공 할 수있는 사용자에 대한 메모리를 할당 할 수 있어야한다 예를 들어 말 진지한 토론을 열어 라.

행운을 빌어 요!

나는 현재 snprintf의 모델과 유사한 기능 후 첫 번째 방법과 유사하게 그것을 할, 그러나 다만 미묘하게 다른 것
+0

대신 스택에 복사하여 해당 버퍼 구조를 전달할 수 없습니다. 동적 메모리를 사용하기에는 다소 어리 석다. (내가하고 있다고 가정한다.) – toto

+1

@toto : 함수가 포인터를 취하기 때문에 버퍼가 동적 메모리가 아니기 때문에 _have_하지 않아야한다. 주소로 전달 된 스택 인스턴스가 될 수 있습니다. 그러나 일반적으로 함수에 전달해야 할 때마다 스택 구조체에 크기와 데이터를 래핑하는 대신 일반 유형으로 버퍼 구조체를 사용하는 것이 좋습니다. 스택으로 선언 된 구조체에 대해 말하고 있다고 가정합니다. – sharkin

+0

죄송합니다. Java가 가지고있는 Java의 일부 손상 일뿐입니다. 포인터는 스택에있는 오브젝트에 적합합니다. – toto

0

:이 많이있는 경우

int func(char* buffer, size_t buffer_size, input params...); 

이 방법, 그들이 유사 할 수 있습니다 유용 할 때마다 가변 인수를 사용할 수 있습니다.

차라리 버전 2보다 버전 1을 사용하는 이미 언급 한 이유에 크게 동의 - 메모리 문제는 버전 2

+0

나는 그가 '...'을 사용하여 줄임표를 추천하고 있다고 생각하지 않습니다. 그 밖의 것이 없다면 줄임표는 항상 인수 목록의 마지막에 와야합니다. – sharkin

+0

나는 그가 그것을 언급하지 않았을 것이라고 확신하지만, 매개 변수를 재배치한다는 생각이 들었다. =) –

1

어떤 두 가지 방법을 사용하는 방법에 대한 훨씬 더 가능성이있다?


// Style 1 functions 
int fooBuff(char* buffer, unsigned int buffer_size, input params...); 

// Style 2 functions 
char* fooBuffAlloc(input params...); 
bool fooBuffFree(char* foo); 

/D

을 : 나는 모든 API의 지금처럼 일관된 명명 관용구를 따르는 경우 스타일 2가 사용될 수있다 생각합니까 스타일 2의 함정 대 스타일 1을 선호하는 응답의 합의에 동의