2016-09-11 3 views
2

C에서는 많은 입력을 필요로하는 함수를 보는 것이 드문 일이 아니며 많은 옵션은 구조체에서 이들을 그룹화하여 인터페이스를보다 명확하게 만듭니다. 개발자. (비록 당신이 컴파일러에 의존 할 수 있어야한다. at least 127 arguments to a function 아무도 실제로 많은 것을 쓰려고한다. 특히 C는 오버로딩이나 기본 함수 인자 지원이 없다.kwargs를 사용하여 선택적 인수의 구조체를 사용하는 함수 줄 바꾸기

#include <stdbool.h> 

typedef struct { 
    const char *name; 
    void *stuff; 
    int max_size; 
    char flags; 
    _Bool swizzle; 
    double frobination; 
    //... 
} ComplexArgs; 

void ComplexFun(const ComplexArgs *arg) {} 

그것을 사용하여 신속하게 작업을 우리가 뭔가를 얻을 수 있습니다 사용하여이 꿀꺽 꿀꺽 포장에 관해서 : 가상의 예를 들어 우리는 문제를 설명하기 위해 다음과 같은 구조체/기능 쌍 (test.h)을 고려할 수

%module test 

%{ 
#include "test.h" 
%} 

typedef bool _Bool; 

%include "test.h" 

import test 

args=test.ComplexArgs() 
args.flags=100; 
args.swizzle=True 
test.ComplexFun(args) 

을하지만 그건 정확히 파이썬되지 않습니다 : 작동하고 다음과 같이 우리가 그것을 사용할 수 있습니다

.

import test 

# Not legal in the interface currently: 
test.ComplexFun(flags=100, swizzle=True) 

우리가 어떻게 그 일을 할 수 있습니다 : 파이썬 개발자는 호출의 종류를 지원하는 데 사용 kwargs로보고 더 익숙한 것입니까? SWIG -keyword 명령 행 옵션은 함수에 실제 인수가 하나만 있기 때문에 도움이되지 않습니다.

답변

4

일반적으로 파이썬에서 함수 인수와 반환 값을 수정하는 방법은 데코레이터를 사용하는 것입니다.

  1. 는 또한 호출하기위한 구문을 아프게하지 않습니다 : 그것은 그런 식으로 기록 된 몇 가지 더 깔끔한 특성을 가지고

    def StructArgs(ty): 
        def wrap(f): 
        def _wrapper(*args, **kwargs): 
         arg=(ty(),) if len(kwargs) else tuple() 
         for it in kwargs.iteritems(): 
         setattr(arg[0], *it) 
         return f(*(args+arg)) 
        return _wrapper 
        return wrap 
    

    : 시작점으로 그 문제를 해결 다음과 같은 장식을, 밖으로 스케치 단일 구조체 인수로 직접 함수

  2. 필수 위치 지정 인수 인 이있는 함수를 선택적 인수로 가득 찬 마지막 인수로 지원할 수 있습니다. (현재 non-struct 필수 인수에 대해 kwargs 구문을 사용할 수는 없지만)

질문은 SWIG에서 생성 된 Python 코드의 올바른 함수에 데코레이터를 적용하는 것 중 하나가됩니다. 내 계획은 가능한 한 가장 간단한 매크로로 마무리하는 것이 었습니다. 패턴이 라이브러리에서 반복되어 많이 포장하고 있기 때문입니다. 그것은 내가 예상했던 것보다 더 어려웠다. 내가 처음 시도 (그리고 난 분명히 not the only one 해요) :

  1. %feature("shadow") - 나는 그 일을 할 확신했다, 실제로는 멤버 함수 C++에 대한 작업을 수행하지만 전역에서 무료로 기능이 작동하지 않습니다 웬일인지 내가 알아 내지 않았어.
  2. %feature("autodoc")%feature("docstring")은 - 낙관적 나는 약간을 남용 할 수 있도록 희망하지만 것이다 기쁨 꿀꺽 꿀꺽이 C 측의 함수 선언을보고 바로 전에
  3. %pythoncode
  4. . 올바른 코드를 생성하지만 불행히도 SWIG는 ComplexFun = _test.ComplexFun을 추가하여 장식 된 함수를 즉시 숨 깁니다. 꽤 오랫동안 그 길을 찾을 수 없었습니다.
  5. 우리가 호출 한 실제 함수를 숨기려면 %rename을 사용하고 장식 된 실제 함수 주위에 래퍼를 작성하십시오.그것은 기본적으로 새로운 래퍼에 작성하는 대신에 위의 데코레이터를 기본적으로 작성하지 않았기 때문에 효과가있었습니다.

마지막으로 나는 자유로운 기능을 꾸미기 위해 더러운 트릭을 발견했다. 함수에서 %pythonprepend을 사용함으로써 # 3이 작동하지 않도록하는 여분의 코드를 억제하기에 충분한 것을 (아무 것도, 주석, pass, 빈 문자열 등) 삽입 할 수있었습니다.

내가 마주 친 마지막 문제는 모든 매크로가 하나의 매크로로 작동하도록하고 %pythoncode 지시어 권한을 얻는 것입니다 (또한 선언을 포함하고있는 헤더 파일의 %include ing을 허용 함). 매크로를 호출해야했습니다. %include 전. 따라서 실제 헤더 파일에서 두 번째로 보이는 경우 함수를 무시하기 위해 추가로 %ignore을 추가해야했습니다. 그러나 소개 한 다른 문제는 구조체 앞에 함수를 감싸는 것입니다. 따라서 파이썬 모듈 내부에서 데코레이터를 필요로하는 구조체의 유형은 아직 데코레이터를 호출 할 때 알려지지 않았습니다. 문자열을 장식자에 유형 대신 전달하고 나중에 찾으려고하면 쉽게 완료 될 수 있습니다 (module globals()). 그래서 그와

말했다이된다 랩 전체, 작업 인터페이스 : 당신은 아마 싶어

import test 

args=test.ComplexArgs() 
args.flags=100; 
args.swizzle=True 
test.ComplexFun(args) 

test.ComplexFun(flags=100, swizzle=True) 

것들 : 이것은 다음 파이썬 코드 작업에 충분했다

%module test 

%pythoncode %{ 
def StructArgs(type_name): 
    def wrap(f): 
    def _wrapper(*args, **kwargs): 
     ty=globals()[type_name] 
     arg=(ty(),) if kwargs else tuple() 
     for it in kwargs.iteritems(): 
     setattr(arg[0], *it) 
     return f(*(args+arg)) 
    return _wrapper 
    return wrap 
%} 

%define %StructArgs(func, ret, type) 
%pythoncode %{ @StructArgs(#type) %} // *very* position sensitive 
%pythonprepend func %{ %} // Hack to workaround problem with #3 
ret func(const type*); 
%ignore func; 
%enddef 

%{ 
#include "test.h" 
%} 

typedef bool _Bool; 

%StructArgs(ComplexFun, void, ComplexArgs) 

%include "test.h" 

이걸 사용하기 전에해야할 일 :

  1. 이 장식 자와 kwargs는 현재 쓰여지고 있습니다. 어떤 종류의 TypeError를 되찾기가 어렵다. 아마도 C 함수는 잘못된 입력 조합을 나타내는 방법을 가지고있을 것입니다. Python 사용자를 위해 TypeError 예외로 변환하십시오.
  2. 필요한 경우 필수 위치 지정 인수를 지원하도록 매크로를 수정하십시오.