일부 플랫폼에서는 char **, 다른 플랫폼에서는 const char **를받는 C ++ 함수를 이식 가능하게 호출 할 수있는 방법은 무엇입니까?
내 Linux (및 OS X) 컴퓨터에서 iconv()
함수에는 다음 프로토 타입이 있습니다.
size_t iconv (iconv_t, char **inbuf...
FreeBSD에서는 다음과 같이 보입니다 :
size_t iconv (iconv_t, const char **inbuf...
내 C ++ 코드를 두 플랫폼 모두에서 빌드하고 싶습니다. C 컴파일러에서 매개 변수에 char**
대해 전달 const char**
(또는 그 반대)은 일반적으로 단순한 경고를 내 보냅니다. 그러나 C ++에서는 치명적인 오류입니다. 따라서를 전달하면 char**
BSD에서 컴파일되지 않고, 전달하면 const char**
Linux / OS X에서 컴파일되지 않습니다. 플랫폼을 감지하지 않고 두 가지 모두에서 컴파일되는 코드를 어떻게 작성할 수 있습니까?
한 가지 (실패한) 아이디어는 헤더에서 제공하는 모든 것을 재정의하는 로컬 프로토 타입을 제공하는 것이 었습니다.
void myfunc(void) {
size_t iconv (iconv_t, char **inbuf);
iconv(foo, ptr);
}
이것은 iconv
C 연결 이 필요 하기 때문에 실패 extern "C"
하고 함수 내에 넣을 수 없습니다 (왜 안됩니까?)
내가 생각 해낸 최고의 작업 아이디어는 함수 포인터 자체를 캐스팅하는 것입니다.
typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);
그러나 이것은 더 심각한 다른 오류를 가릴 가능성이 있습니다.
원하는 것이 const 문제에 눈을 멀게하는 것이라면 구분을 흐리게하는 변환을 사용할 수 있습니다. 즉, char ** 및 const char **를 상호 운용 가능하게 만듭니다.
template<class T>
class sloppy {};
// convert between T** and const T**
template<class T>
class sloppy<T**>
{
T** t;
public:
sloppy(T** mt) : t(mt) {}
sloppy(const T** mt) : t(const_cast<T**>(mt)) {}
operator T** () const { return t; }
operator const T** () const { return const_cast<const T**>(t); }
};
그런 다음 나중에 프로그램에서 :
iconv(c, sloppy<char**>(&in) ,&inlen, &out,&outlen);
sloppy ()는 a char**
또는 a const char*
를 받아서 iconv의 두 번째 매개 변수가 요구하는대로 a char**
또는 a 로 변환합니다 const char*
.
업데이트 : const_cast를 사용하고 as cast가 아닌 sloppy를 호출하도록 변경되었습니다.
선언 된 함수의 시그니처를 검사하여 두 선언을 명확하게 할 수 있습니다. 다음은 매개 변수 유형을 검사하는 데 필요한 템플릿의 기본 예입니다. 이것은 쉽게 일반화 될 수 있지만 (또는 Boost의 기능 특성을 사용할 수 있음) 특정 문제에 대한 솔루션을 입증하기에 충분합니다.
#include <iostream>
#include <stddef.h>
#include <type_traits>
// I've declared this just so the example is portable:
struct iconv_t { };
// use_const<decltype(&iconv)>::value will be 'true' if the function is
// declared as taking a char const**, otherwise ::value will be false.
template <typename>
struct use_const;
template <>
struct use_const<size_t(*)(iconv_t, char**, size_t*, char**, size_t*)>
{
enum { value = false };
};
template <>
struct use_const<size_t(*)(iconv_t, char const**, size_t*, char**, size_t*)>
{
enum { value = true };
};
다음은 동작을 보여주는 예입니다.
size_t iconv(iconv_t, char**, size_t*, char**, size_t*);
size_t iconv_const(iconv_t, char const**, size_t*, char**, size_t*);
int main()
{
using std::cout;
using std::endl;
cout << "iconv: " << use_const<decltype(&iconv) >::value << endl;
cout << "iconv_const: " << use_const<decltype(&iconv_const)>::value << endl;
}
매개 변수 유형의 규정을 감지 할 수 있으면 호출하는 두 개의 래퍼 함수를 작성할 수 있습니다 iconv
. 하나 iconv
는 char const**
인수로 호출하고 다른 하나 iconv
는 char**
인수로 호출합니다 .
함수 템플릿 전문화는 피해야하므로 클래스 템플릿을 사용하여 전문화합니다. 또한 우리가 사용하는 전문화 만 인스턴스화되도록 각 호출자를 함수 템플릿으로 만듭니다. 컴파일러가 잘못된 전문화에 대한 코드를 생성하려고하면 오류가 발생합니다.
그런 다음 call_iconv
이를 iconv
직접 호출하는 것처럼 간단하게 호출하기 위해 사용을 래핑합니다 . 다음은 이것이 작성되는 방법을 보여주는 일반적인 패턴입니다.
template <bool UseConst>
struct iconv_invoker
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
template <>
struct iconv_invoker<true>
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
size_t call_iconv(/* arguments */)
{
return iconv_invoker<
use_const<decltype(&iconv)>::value
>::invoke(&iconv, /* arguments */);
}
(이 후자의 논리는 정리되고 일반화 될 수 있습니다. 저는 그것이 작동하는 방식을 더 명확하게하기 위해 각 부분을 명시 적으로 만들려고 노력했습니다.)
다음을 사용할 수 있습니다.
template <typename T>
size_t iconv (iconv_t i, const T inbuf)
{
return iconv(i, const_cast<T>(inbuf));
}
void myfunc(void) {
const char** ptr = // ...
iconv(foo, ptr);
}
통과 할 수 있으며 const char**
Linux / OSX에서는 템플릿 기능을 통과하고 FreeBSD에서는 iconv
.
단점 : iconv(foo, 2.5)
컴파일러를 무한 반복 상태로 만드는 것과 같은 호출을 허용합니다 .
#ifdef __linux__
... // linux code goes here.
#elif __FreeBSD__
... // FreeBSD code goes here.
#endif
여기 에 모든 운영 체제의 ID가 있습니다. 저에게는이 시스템을 확인하지 않고 운영 체제에 의존하는 작업을 시도 할 필요가 없습니다. 초록색 바지를 사는 것과 같지만 보지 않고.
자신의 래퍼 기능을 사용할 수 있음을 표시했습니다. 당신은 또한 기꺼이 경고와 함께 살고있는 것 같습니다.
따라서 래퍼를 C ++로 작성하는 대신 C로 작성하면 일부 시스템에서만 경고가 표시됩니다.
// my_iconv.h
#if __cpluscplus
extern "C" {
#endif
size_t my_iconv( iconv_t cd, char **restrict inbuf, ?* etc... */);
#if __cpluscplus
}
#endif
// my_iconv.c
#include <iconv.h>
#include "my_iconv.h"
size_t my_iconv( iconv_t cd, char **inbuf, ?* etc... */)
{
return iconv( cd,
inbuf /* will generate a warning on FreeBSD */,
/* etc... */
);
}
어때
static void Test(char **)
{
}
int main(void)
{
const char *t="foo";
Test(const_cast<char**>(&t));
return 0;
}
편집 : 물론 "플랫폼을 감지하지 않고"는 약간의 문제입니다. 죄송합니다 :-(
편집 2 : 좋아, 개선 된 버전일까요?
static void Test(char **)
{
}
struct Foo
{
const char **t;
operator char**() { return const_cast<char**>(t); }
operator const char**() { return t; }
Foo(const char* s) : t(&s) { }
};
int main(void)
{
Test(Foo("foo"));
return 0;
}
이건 어떤가요:
#include <cstddef>
using std::size_t;
// test harness, these definitions aren't part of the solution
#ifdef CONST_ICONV
// other parameters removed for tediousness
size_t iconv(const char **inbuf) { return 0; }
#else
// other parameters removed for tediousness
size_t iconv(char **inbuf) { return 0; }
#endif
// solution
template <typename T>
size_t myconv_helper(size_t (*system_iconv)(T **), char **inbuf) {
return system_iconv((T**)inbuf); // sledgehammer cast
}
size_t myconv(char **inbuf) {
return myconv_helper(iconv, inbuf);
}
// usage
int main() {
char *foo = 0;
myconv(&foo);
}
I think this violates strict aliasing in C++03, but not in C++11 because in C++11 const char**
and char**
are so-called "similar types". You aren't going to avoid that violation of strict aliasing other than by creating a const char*
, set it equal to *foo
, call iconv
with a pointer to the temporary, then copy the result back to *foo
after a const_cast
:
template <typename T>
size_t myconv_helper(size_t (*system_iconv)(T **), char **inbuf) {
T *tmpbuf;
tmpbuf = *inbuf;
size_t result = system_iconv(&tmpbuf);
*inbuf = const_cast<char*>(tmpbuf);
return result;
}
This is safe from the POV of const-correctness, because all iconv
does with inbuf
is increment the pointer stored in it. So we're "casting away const" from a pointer derived from a pointer that was non-const when we first saw it.
We could also write an overload of myconv
and myconv_helper
that take const char **inbuf
and messes things about in the other direction, so that the caller has the choice whether to pass in a const char**
or a char**
. Which arguably iconv
should have given to the caller in the first place in C++, but of course the interface is just copied from C where there's no function overloading.
Update: now I see that it is possible to handle it in C++ without autotools, yet I'm leaving the autoconf solution for people looking for it.
What you're looking for is iconv.m4
which is installed by gettext package.
AFAICS it's just:
AM_ICONV
in configure.ac, and it should detect the correct prototype.
Then, in the code you use:
#ifdef ICONV_CONST
// const char**
#else
// char**
#endif
I am late to this party but still, here is my solution:
// This is here because some compilers (Sun CC) think that there is a
// difference if the typedefs are not in an extern "C" block.
extern "C"
{
//! SUSv3 iconv() type.
typedef size_t (& iconv_func_type_1) (iconv_t cd, char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft);
//! GNU iconv() type.
typedef size_t (& iconv_func_type_2) (iconv_t cd, const char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft);
} // extern "C"
//...
size_t
call_iconv (iconv_func_type_1 iconv_func, char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft)
{
return iconv_func (handle, inbuf, inbytesleft, outbuf, outbytesleft);
}
size_t
call_iconv (iconv_func_type_2 iconv_func, char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft)
{
return iconv_func (handle, const_cast<const char * *>(inbuf),
inbytesleft, outbuf, outbytesleft);
}
size_t
do_iconv (char * * inbuf, size_t * inbytesleft, char * * outbuf,
size_t * outbytesleft)
{
return call_iconv (iconv, inbuf, inbytesleft, outbuf, outbytesleft);
}
'programing tip' 카테고리의 다른 글
$ (document.body)와 $ ( 'body') JQuery의 차이점 (0) | 2020.09.04 |
---|---|
스톱워치 대 System.DateTime.Now를 사용하여 타이밍 이벤트 (0) | 2020.09.04 |
scala.concurrent.Promise의 사용 사례는 무엇입니까? (0) | 2020.09.04 |
except 블록을 테스트하기 위해 Exception을 발생시키는 함수 모킹 (0) | 2020.09.04 |
xml을 사용하여 Android TextView에서 사용자 정의 글꼴 사용 (0) | 2020.09.04 |