2009-11-11 2 views
13

D의 static if은 흥미로운 언어 기능이라고 생각합니다. 컴파일러가 코드에 대한 강력한 개념을 갖고 있고 액세스 할 수있는 언어 기능이있는 컴파일 된 언어의 다른 예가 있습니까? 예를 들어정적 인 경우 D 외에 다른 언어가 있습니까?

,이 코드는 파이썬에서 repr 비슷한을 제공합니다

char[] repr(T)(T value) { 
    static if (is(typeof(value.__repr__))) { // class T provides a "repr()" method 
    return value.__repr__(); 
    } else static if (is(T:string)) { 
    return `"` ~ value ~ `"`; 
    // ...other cases... 
    } else { 
    return toString(value); 
    } 
} 

내가이 멋진 생각입니다 그것은 수 있기 때문에 내부의 종류 무엇인지 과부하 수행 할 수있는 다른 더 일반적인 접근, 이와 같은 기능에 비해 코드를보다 동적으로 만들 수 있습니다. 예를 들어, 컴파일러는 내 클래스의 필드 수를 알고 있지만, 대부분의 언어로 컴파일 할 때 내 정보에 액세스 할 수있는 방법은 없습니다.

경고 : 마지막 단락에는 의견이 있었지만 논쟁을 불러 일으키지 않고 내 질문에 대한 동기와 설명을 제공하기 만하면됩니다. 다른 컴파일 된 언어에 이러한 기능이 있는지 확인하고 싶습니다.

+1

많은 C 및 C++ 컴파일러는 'if'의 표현식이 컴파일 타임에 결정될 수 있다면 사용되지 않는 코드를 제거합니다. 이 질문에 완전히 대답해도 확실하지 않습니다. –

+0

아니요 - 정적 if의 일부 블록에있는 코드가 유효하지 않기 때문에 위의 예는 컴파일되지 않습니다. 예 : T가 int 유형이면 값 .__ repr __()은 컴파일 오류입니다. – Grumdrig

+1

위의 컴파일을 위해 "정적 if (type if (value .__ repr__))")를 작성해야합니다. 또는 "정적 if (__ traits (compiles, value .__ repr__))"). – Baxissimo

답변

10

실제 매크로가있는 언어는 정적 인 if 형식을가집니다. 예를 들어 Lisp과 Nemerle은 'if'와 for-loops와 같은 프로그래밍 구조를 사용하여 매크로가 확장되는 코드를 구성하게합니다. 그것들은 본질적으로 컴파일 타임의 결정이며 정적 인 경우와 비슷한 것을 할 수 있습니다. Nemerle 매크로의 경우 매크로는 기본적으로 컴파일 타임에 실행되는 컴파일러에 대한 플러그인입니다.

C++에는 두 유형 중 하나를 선택하는 데 사용할 수있는 kind of static if이있는 boost MPL 라이브러리가 있습니다. run() 멤버에 두 가지 유형의 코드를 넣고 비슷한 코드를 얻을 수 있지만 매우 성가신 구문입니다. D에서

struct float_impl { 
    static void run() { /* float case code */ } 
} 
struct int_impl { 
    static void run() { /* int case code */ } 
} 

typedef typename if_< 
      is_same<T, float> 
     , float_impl 
     , int_impl 
     >::type impl_t; 
impl_t::run(); 

것 :

가 부스트 MPL과 함께 예를 들어 이런 식으로 뭔가 할 수있는 "코드의 언어의 인식"에 대한

static if(is(T == float)) { 
    /* float code */ 
} 
else { 
    /* int code */ 
} 
+0

아름다운. Nemerle은 나에게 새롭고 매우 흥미 롭습니다. 앞으로 살펴보기를 기대합니다. 내가 생각해야만했던 Lisp 매크로. – Grumdrig

2

를, 더 좋은 I가 없습니다 Lisp과 매크로 기능, 특히 Common Lisp보다 더 많이 보입니다. 그러나 거기에서의 거래는 대부분 객체의 유형이 컴파일 타임이나 거시 확장 시간에 알려지지 않았기 때문입니다. 리터럴의 경우 유형이 알려져 있기 때문에 유형이 리터럴인지 여부를 테스트하는 공격적인 매크로의 예를 찾을 수 있으며, 그렇다면 유형에 따라 한 가지 방법으로 처리합니다. 그렇지 않으면 감지 된 변수를 준비합니다 런타임 유형 검사를 위해.

몇 년 전에 CLLIB 라이브러리 (CLOCC 라이브러리의 일부)에서 수정 한 예입니다. 목표는 접두어 문자열을 일치하는 접두사가있는 다른 문자열에서 잘라내는 기능을 제공하는 것입니다. 접두사는 매크로 확장 시간에 알려 지거나 접두사가 없을 수도 있습니다. 그렇다면 접두사의 길이를 먼저 계산하여 리터럴로 포함시켜 생성 된 함수를 호출 할 때마다 다시 계산되지 않도록 최적화 할 수 있습니다. 매크로는 처음에는 힘들지만 실제로 생성 된 코드는 작습니다.

(defmacro after-prefix-core (comparison-op prefix string &optional length) 
    "Similar to cllib:string-beg-with-cs." 
    (flet ((chop (prefix prefix-length string string-length) 
      `(when (and (>= ,string-length ,prefix-length) 
         (,comparison-op ,prefix ,string :end2 ,prefix-length)) 
       (subseq ,string ,prefix-length ,string-length)))) 
    (let* ((gstring (gensym "STRING-")) 
      (gstring-length (gensym "STRING-LENGTH-"))) 
     `(let* ((,gstring ,string) 
       (,gstring-length ,(or length `(length ,gstring)))) 
     ,(if (stringp prefix) 
       ;; Constant -- length known at expansion time. 
       (let ((prefix-length (length prefix))) 
       (chop prefix prefix-length gstring gstring-length)) 
       ;; Other form -- length not known at expansion time. 
       (let ((gprefix (gensym "PREFIX-")) 
        (gprefix-length (gensym "PREFIX-LENGTH-"))) 
       `(let* ((,gprefix ,prefix) 
         (,gprefix-length (length ,gprefix))) 
        ,(chop gprefix gprefix-length gstring gstring-length)))))))) 


(defmacro after-prefix (prefix string &optional length) 
    "Similar to cllib:string-beg-with." 
    `(after-prefix-core string-equal ,prefix ,string ,length)) 


(defmacro after-prefix-cs (prefix string &optional length) 
    "Similar to cllib:string-beg-with-cs." 
    `(after-prefix-core string= ,prefix ,string ,length)) 

양식 중간에

(if (stringp prefix) 

를 참조하십시오? 이것은 매크로 확장 시간에 첫 번째 인수를 검사하고 인수가 리터럴인지 기호인지 여부에 따라 유형을 알 수도 있고 아닐 수도 있습니다.형식이 기호 인 경우 은 실행 시간까지 다른 값을 가리키는 변수로 다시 고려해야 할 때까지 기다려야하는으로 가정합니다. 가변 #:PREFIX-LENGTH-5343 가변 #:PREFIX-5342 여기 결합FOO계산 된 길이에 결합되어

(LET* ((#:STRING-5340 BAR) (#:STRING-LENGTH-5341 (LENGTH #:STRING-5340))) 
    (LET* ((#:PREFIX-5342 FOO) (#:PREFIX-LENGTH-5343 (LENGTH #:PREFIX-5342))) 
    (WHEN 
     (AND (>= #:STRING-LENGTH-5341 #:PREFIX-LENGTH-5343) 
      (STRING-EQUAL #:PREFIX-5342 #:STRING-5340 :END2 #:PREFIX-LENGTH-5343)) 
     (SUBSEQ #:STRING-5340 #:PREFIX-LENGTH-5343 #:STRING-LENGTH-5341)))) 

참고

여기 (after-prefix foo bar) 형태에 대한 확장이다.

지금 접두사가 지금 리터럴 문자열 형태 (after-prefix "foo" bar)에 대한 확장 보면 :

(LET* ((#:STRING-5463 BAR) (#:STRING-LENGTH-5464 (LENGTH #:STRING-5463))) 
    (WHEN (AND (>= #:STRING-LENGTH-5464 3) (STRING-EQUAL "foo" #:STRING-5463 :END2 3)) 
    (SUBSEQ #:STRING-5463 3 #:STRING-LENGTH-5464))) 

이제 더 컴퓨팅 "foo는"길이 없다; 그것은 3으로 인라 인됩니다.

이 예제에서는 너무 많은 작업처럼 보일 수 있지만 그런 것들을 할 수있는 것은 좋은 질문입니다.

관련 문제