2016-08-04 4 views
1

이것은 함수 인수가 표현식에서 평가 될 때 부작용과 관련하여 표준 C11의 표준에 대한 질문입니다.함수 호출에서 잘 형성된 쌍이

OOP 언어의 "방법"과 유사한 구문을 모방하는 표준 C에서 매크로를 정의하려고합니다.
나는 여기에 설명 할 주요 아이디어가있는 솔루션을 설계했지만 C11에 대한 적합성에 대해서는 약간의 의구심이있다.
먼저 해설을해야하고 마지막에는 함수 호출과 관련된 표현식 평가와 관련된 특정 질문을 할 것입니다. 긴 게시물에 의해 죄송합니다. 잘 struct, 또는 주어진 그래서

, 나는이 방법으로는 "방법"전화를 할 수있는 경우 struct * 객체 x, 나는 행복 할 것 :

x->foo_method(); 

전형적인 방식이이 문제에

:

  • struct 선언에 의해 "클래스"를 정의합니다 해결되는 같은 것입니다 691,363,210

    typedef struct foo_s { void foo_method(struct foo_s * this); } *foo_class; 
    foo_class x = malloc(sizeof(struct foo_s)); 
    x->foo_method = some_function_defined_over_there___; 
    
  • 는 그 다음``이 ''매개 변수에 객체를 반복하여 전화를 걸 :

    x->foo_method(x); 
    

하나는 "메소드 호출"어떤 종류의 정의를 시도 할 수 있습니다 매크로 :

#define call(X, M) ((X)->M(X)) 

X의 평가가 부작용을 복제 할 수 있으므로 (매크로 매개 변수를 두 번 반복하는 잘 알려진 함정)이 방법은 좋지 않습니다.

이 반복되는 문제를 해결하기 [I이 방법 M 대한 파라미터의 임의의 수의 경우를 처리 할 수있는 힘들 매크로를 사용하여, 예를 들면, __VA_ARGS__ 중간 매크로 해킹의 일부를 이용하여.] 한 번만`X를 writtin에 의해

(void*) my_stack(void* x, char* operation) 
{ 
    static void* stack[100] = { NULL, }; 
    // 'operation' selects "push" or "pop" operations on the stack. 
    // ... 
    // IF (operation == "push") then 'x' itself is returned again. 
} 

그래서 지금, 나는 매크로 부작용의 중복을 피하기 : 매크로 인수, 나는 어쩌면 함수의 정적 배열로 숨겨진 글로벌 스택을 구현하기로 결정 :

#define call(X, M) (((foo_class)my_stack((X), "push")) -> M (my_stack(0,"pop"))) 

알 수 있듯이 제 의도는 함수와 비슷한 매크로가 C 컴파일러에 의해 표현식으로 간주되는데 그 값은 메서드 M에 의해 반환 된 값입니다.
매크로 바디 안에 X 매개 변수를 한 번만 썼습니다. 그 값은 스택에 저장되었습니다. 이 값이 X이라는 "method"멤버에 액세스 할 수 있어야하므로이 이유는 my_stack 함수가 x 값을 반환하는 이유입니다. 값을 밀어 넣은 동일한 표현식의 일부로 즉시 다시 사용해야합니다. x 스택에.이 아이디어는 쉽게 call(X,M) 매크로 X의 중복의 문제를 해결하는 것 같지만


, 그들은 더 문제가 나타납니다.

  • 동일한 매크로 call()을 사용하여 인수가 스택에 저장된 개체 인 "메서드"를 하나 이상 가질 수 있습니다.
  • 우리는 "메소드"에서 다른 "메소드"를 평가 한 결과 값을 얻을 수있는 인수를 가질 수 있습니다.
  • 마지막으로 call() 매크로를 사용하여 스택을 수정하는 함수이기 때문에 주어진 "메서드"의 인수로 나타나는 다른 함수 나 메서드가 스택을 수정할 수 있습니다.

모든 경우 내 매크로가 일관성있게 유지되기를 바랍니다. 예를 들어 x1,x2,x3foo_class 개체라고 가정 해 보겠습니다.
한편 , 우리가 foo_class에, 우리가 가지고 있다고 가정하자 다음 "방법"회원 :

call(x1, meth, (call (x2, 2, 2), call(x3, 3, 3))) ; 

: 마지막으로

int (*meth)(foo_class this, int, int); 

, 우리는 "방법"전화를 걸 수 [매크로에 대한 실제 구문은 반드시 거기에 표시된대로 필요하지 않습니다. 나는 주요 성분은 이해 것으로 예상]

의도는이 함수 호출을 모방하는 것입니다.

x1->meth(x1, x2->meth(x2,2,2), x3->meth(x3,3,3)); 

여기서 문제는 내가 개체의 다음 중복을 모방하기 위해 스택을 사용하고 있다는 것입니다 전화 : x1->meth(x1,....), x2->meth(x2,...), x3->meth(x3,...).

예 : ((foo_class)(my_stack(x2,"push"))) -> meth (my_stack(0,"pop"), ...).

내 질문은 : 내가 모든 가능한 표현의 페어링 "푸시"/ "팝업"이 제공하는 모든 시간 객체의 예상 쌍 (즉, 지속적으로 call() 매크로를 사용) 항상 확신 할 수 있습니까?

예를 들어, x2를 "밀고"있으면 x3이 "튀어 나올"것이 완전히 잘못됩니다.

내 추측은 다음과 같습니다 대답은 YES,하지만 시퀀스의 주제에 표준 문서 ISO C11의 깊은 분석 후 포인트 것입니다.

  • 호출 할 "메서드"(실제로 "함수")를 생성하는 표현식과 전달할 인수의 표현식 사이에는 시퀀스 포인트가 있습니다. 따라서 meth 메서드가 호출되기 전에 스택에 x1이 저장됩니다.
  • 함수에 전달 된 모든 인수를 평가 한 후 실제 함수 호출 전에 시퀀스 포인트가 있습니다.
    예를 들어, 새 오브젝트 x4, x5 등이있는 경우x1->meth(x1...x2...x3)에 대한 호출이 발생하는 동안 스택에 "밀어 넣기"/ "팝"됩니다. x4x5은 스택에서 이미 나타 났으며 x2, x3 이후 사라집니다. 스택에서 이미 사라졌습니다.
  • 함수 호출에서 인수 사이에 시퀀스 포인트가 없습니다. (가 위의 함수 호출 argurments이 때 x1,x2,x3 관련된)
    따라서, 다음 표현식은 평가 된 때 인터리빙 수 :

    my_stack(x2,"push") -> meth(my_stack(0,"pop"),2,2) 
    my_stack(x3,"push") -> meth(my_stack(0,"pop"),3,3) 
    

    객체 x2 및 될 후에 "푸시 것이 일어날 수 "스택에서"팝 "작업은 병합되어 발생할 수 있습니다. 은 meth(...,2,2) 행에"팝 "될 수 있으며 x2 행은 원하는 경우와 비교하여 meth(...,3,3) 행에서"팝 "될 수 있습니다.

    이 상황은 전혀 발생하지 않으며 표준 C99에서는 공식적인 해결책이없는 것처럼 보입니다.

그러나 C11에는 inderminately sequenced이라는 부작용이 있습니다.

함수가 호출
  • 은, 모든 부작용 indeterminaly 존경을 함수 호출을 표현 주변의 다른 표현 염기 서열을 해결 :
    우리는 가지고있다. [(12) 항을 참조하십시오 : sequence points]. 식에 참여 meth의 함수 호출의 부작용 이후

  • :

    my_stack(x3,"push") -> meth(my_stack(0,"pop"),3,3) 
    
    :

    my_stack(x2,"push") -> meth(my_stack(0,"pop"),2,2) 
    

    는 "완전히 후"부작용에 "완전히 전에"해결하거나이

    "밀어 넣기"및 "팝"작업은 잘 짝을 이루어이라고 결론지었습니다.

표준에 대한 나의 해석은 괜찮습니까? 난 그냥 경우에, 그것을 인용합니다 :

는 [C11는 6.5.2.2/10] 기능 지정자의 평가와 실제 인수 후에하지만 실제 호출하기 전에 일련의 지점이있다. 호출 된 함수의 본문 실행 전후에 특별히 순서화되지 않은 호출 함수 (함수 호출 포함)의 모든 평가는 호출 된 함수의 실행과 관련하여 불확실하게 순서가 지정됩니다 .94)
[각주 94 ] : 즉, 함수 실행은 서로 인터리브하지 않습니다.

즉, 함수 호출에서 인수의 평가 순서는 예측할 수 없지만 ISO C11에서 정의 된 "시퀀스"규칙이 다음을 보장 할만큼 충분하다는 것을 확신합니다. 이 경우 "푸시"및 "팝"조작이 잘 작동합니다.

그래서 "메서드의"개체의 기본 OOP 기능을 에뮬레이트하기 위해 C에서 "메서드"와 같은 구문을 사용할 수 있습니다.

+2

먼저 포인터를'typedef'하지 마십시오! 그것은 단지 혼란을 가져옵니다. 둘째 : 매크로를 너무 좋아하지 마라. 당신은 C에서 OOP를 할 수 있지만, C++ 같은 다른 언어를 사용하는 라인이 더 나은 선택입니다. 전제는 가독성/유지 보수성이어야합니다 (OOP를 실제로 사용하는 이유입니다). 나는 한두번 같은 함정에 빠졌다. – Olaf

+1

오, 질문은 스택 오버플로에 적합하지 않습니다. 특정 질문보다 논의가 더 많습니다 (적어도 토론 된 의견/답변을 이끌어 낼 것입니다.) 토론 포럼이 아니라 Q & A 사이트입니다. [ask ]. – Olaf

+0

@Olaf : C에서 OOP를 사용하는 것에 대한 조언을 알고 있습니다.하지만 OOP 측면을 최소한으로 사용하면 C++을 많이 배우지 않고도 C에서 최소한 기초적인 OOP를 가질 필요가있을 수 있습니다 물론 더 정교한 OOP 기술이 필요하다면 C++을 사용하는 것이 필수적이다. – pablo1977

답변

2

아니, 난 당신이 당신이 우리가 B와 C의

my_stack(x2,"push") -> meth(my_stack(0,"pop"),2,2) 
<<<<<<A>>>>>>>>>>   <<<<<<<B>>>>>> 
<<<<<<<<<<<<<C>>>>>>>>>>> 
<<<<<<<<<<<<<<<<<<<<<<<D>>>>>>>>>>>>>>>>>>>>>>>> 

평가서는 완전히 독립적 표현식 분해 want.Let 무엇을한다는 것을 보장 할 수 있다고 생각하지 않고 모두 전에 완료해야합니다 함수 호출 D. 함수의 인수와 함수 지정자는 그다지 다르지 않습니다.

A와 B는 함수 호출이므로 실제로 순서가 지정되어 있지만 실제로는 순서가 정해지지 않으므로 어느 것이 먼저오고 어떤 것이 먼저 올지 알 수 없습니다.

call을 인라인 기능으로 설정하는 것이 훨씬 나을 것이라고 생각합니다. 서로 다른 유형의 버전이 실제로 call 인 경우 _Generic 표현식을 사용하여 함수를 선택할 수 있습니다. 그러나 누군가가 이미 코멘트에서 말했듯이, 당신은 C에서 합리적으로해야하는 것의 한계에 정말로 처해 있습니다.

+0

답변 해 주셔서 감사합니다.그러나 표준에서는 함수 지정자 다음에 시퀀스 포인트가 있다고 주장하므로 의심 스럽습니다. [6.5.2.2/10] :'함수 지정자와 실제 인수를 평가 한 후 실제 호출 전에 시퀀스 포인트가 있습니다. '** function designator **와 ** argument **의 평가가 어떤 순서로든 수행 될 수 있다는 의미인가? 예를 들어, 먼저 인수가 평가되고 두 ​​번째로 함수 지정자는? – pablo1977

+1

예, 인용하고있는 텍스트에서 볼 수 있듯이 함수 지정자와 인수는 순서가없는 표현식의 한 그룹을 형성합니다. 그런 다음 함수 호출 자체가 포함되어 있기 때문에 불확실하게 시퀀스됩니다. 그러나 명령은 고정되어 있지 않습니다. –

+0

@ pablo1977에서 Jens의 말을 되풀이합니다. 이제는 표준 조항에 대해 두 번 똑같은 거짓 주장을했기 때문입니다. 함수 지정자 평가와 함수 인수 평가 사이에는 순서 지점이 없습니다. 표준은 오히려 * all *이 평가 된 후에 시퀀스 포인트가 있다고 말합니다. 상대적인 순서로 평가 될 수 있습니다. –