2015. 8. 11. 00:11

Why Does Qt Use Moc for Signals and Slots?

Why Does Qt Use Moc for Signals and Slots?

왜 Qt 는 시그널과 슬롯으로 Moc 를 사용하나요?

출처 : http://doc.qt.io/qt-5/why-moc.html

Templates are a builtin mechanism in C++ that allows the compiler to generate code on the fly, depending on the type of the arguments passed. As such, templates are highly interesting to framework creators, and we do use advanced templates in many places in Qt. However, there are limitations: There are things that you can easily express with templates, and there are things that are impossible to express with templates. A generic vector container class is easily expressible, even with partial specialisation for pointer types, while a function that sets up a graphical user interface based on a XML description given as a string is not expressible as template. And then there is gray area in between. Things that you can hack with templates at the cost of code size, readability, portability, usability, extensability, robustness and ultimately design beauty. Both templates and the C preprocessor can be stretched to do incredibility smart and mind boggling things. But just because those things can be done, does not necessarily mean doing them is the right design choice. Code unfortunately is not meant to be published in books, but compiled with real-world compilers on real-world operating system.

템플릿은 전달되는 매계변수(인자)의 종류에 따라, 컴파일러가 곧바로 코드를 만들 수 있게 해주는 C++ 의 기본 메커니즘입니다. 그러므로 프레임워크 제작자로 부터 높은 관심을 받았고, 우리도 Qt 의 많은 부분을 고급 템플릿을 사용하고 있습니다. 하지만, 몇가지 제한이 있습니다 : 템플릿을 써서 쉽게 표현할 수 있지만, 템플릿을 써서 표현이 불가능한 것들이 있습니다. 비록 문자열로 주어진 XML 설명에 기반한 그래픽 유저 인터페이스를 설정하는 함수를 템플릿으로 표현할 수는 없는 반면에, 포인터 형식들에 대한 부분 특수화를 포함한 일반적인(제네릭) 벡터 컨테이너 클래스는 쉽게 표현할 수 있습니다. 그리고 그 둘(클래스, 함수) 사이에는 중간 (회색) 지역이 존재합니다. 당신을 코드의 크기, 가독성, 이식성, 유용성, 확장성, 견고함과 극한의 디자인 미각을 고려하여 템플릿을 파고들(해킹할) 수 있습니다. 템플릿과 C 전처리기를 사용하면 믿을수 없는 깔끔함과 정신이 깜짝들 정도로 개발을 진행할 수 있습니다. 하지만, 그렇게 할 수 있다고 해서, 그것이 올바른 선택으로 설계된다는 걸 의미 하지 않습니다. 불행하게도 코드는 책에 쓰여지는 (이론의) 것이 목적이 아니고, 현존하는 운영체제의 컴파일러에 의해 (실제로) 컴파일 됩니다.

Here are some reasons why Qt uses the moc:

여기서 부터는 Qt 가 moc (메타-오브젝트 컴파일러) 를 사용하는 몇가지 이유입니다.

Syntax Matters

문법이 중요합니다.

Syntax isn't just sugar: the syntax we use to express our algorithms can significantly affect the readability and maintainability of our code. The syntax used for Qt's signals and slots has proved very successful in practice. The syntax is intuitive, simple to use and easy to read. People learning Qt find the syntax helps them understand and utilize the signals and slots concept -- despite its highly abstract and generic nature. This helps programmers get their design right from the very beginning, without even having to think about design patterns.

문법은 단순히 설탕 (조미료) 수준이 아닙니다 : 우리의 알고리즘을 표현하는 문법은 코드의 가독성과 유지보수성에 큰 영향을 끼칩니다. Qt 의 시그널과 슬롯에 사용되는 문법은 실전에서 아주 성공적임에 증명되었습니다. 문법은 직관적이고, 쓰기도 단순하고 읽기도 쉽습니다. 비록 아주 추상적이로 일반화된 상태이지만 - Qt 를 배우는 사람들은 시그널과 슬롯 개념을 활용하고 이해하는 문법을 발견 합니다. 이를 통해 디자인 패턴을 생각하지 않아도, 아주 초기에 자신만의 디자인을 갖게 도와줍니다.

Code Generators are Good

코드 생성기는 좋습니다.

Qt's moc (Meta Object Compiler) provides a clean way to go beyond the compiled language's facilities. It does so by generating additional C++ code which can be compiled by any standard C++ compiler. The moc reads C++ source files. If it finds one or more class declarations that contain the Q_OBJECT macro, it produces another C++ source file which contains the meta object code for those classes. The C++ source file generated by the moc must be compiled and linked with the implementation of the class (or it can be #included into the class's source file). Typically moc is not called manually, but automatically by the build system, so it requires no additional effort by the programmer.

Qt 의 moc (메타 오브젝트 컴파일러) 는 컴파일된 언어의 수용 방식을 뛰어 넘는 깔끔한 방법을 제공합니다. 어떠한 표준 C++ 컴파일러도 컴파일이 될 수 있는 추가적인 C++ 코드를 생성하므로 이뤄집니다. moc 는 C++ 소스 파일들을 읽고 그중 하나 이상의 클래스가 Q_OBJECT 매크로를 포함하면, 그것 (moc) 은 그들 클래스들을 위해 메타 오브젝트 코드를 포함한 또다른 C++ 소스 파일을 생성합니다. moc 에 의해 만들어진 C++ 소스 파일은 구현된 클래스와 함꼐 컴파일 되고 링크 과정을 거쳐야 합니다. (또는 해당 클래스의 소스파일에 #include 로 포함될 수 있습니다.) 일반적으로 moc 는 수동으로 호출하지 않고, 빌드 시스템에 자동으로 실행되므로 프로그래머의 추가적인 노력을 요구하지 않습니다.

The moc is not the only code generator Qt is using. Another prominent example is the uic (User Interface Compiler). It takes a user interface description in XML and creates C++ code that sets up the form. Outside Qt, code generators are common as well. Take for example rpc and idl, that enable programs or objects to communicate over process or machine boundaries. Or the vast variety of scanner and parser generators, with lex and yacc being the most well-known ones. They take a grammar specification as input and generate code that implements a state machine. The alternatives to code generators are hacked compilers, proprietary languages or graphical programming tools with one-way dialogs or wizards that generate obscure code during design time rather than compile time. Rather than locking our customers into a proprietary C++ compiler or into a particular Integrated Development Environment, we enable them to use whatever tools they prefer. Instead of forcing programmers to add generated code into source repositories, we encourage them to add our tools to their build system: cleaner, safer and more in the spirit of UNIX.

moc 는 Qt 가 사용하는 유일한 코드 생성기는 아닙니다. 다른 중요한 예는 uic (유저 인터페이스 컴파일러) 입니다. 그것은 XML 로 구성된 사용자(유저) 인터페이스 내용을 폼으로 만들어 주는 C++ 코드를 생성해줍니다. Qt 외부 영역에서도, 코드 생성기들은 또한 공통으로 사용됩니다. 프로세스 또는 기계 경계를 거쳐 프로그램이나 오브젝트 간에 통신을 가능해주는 rpc 와 idl 가 그 예입니다. 또는 다양한 스캐너와 파서 재너레이터 (문장을 읽은 뒤 해석하고 내용을 재생성하는 기계) lex 와 yacc 가 잘 알려진 예입니다. 이들은 입력으로 문법 규칙을 입력하고 상태 기계(스테이트 머신)를 구현하는 코드를 생성합니다. 코드 생성기의 다른 용도로 컴파일 시간 대신 디자인 시간 동안 단방향으로 대화창이나 마법사를 사용해 모호한 코드를 생성하는 컴파일러, 상용 언어나 그래픽 프로그래밍 도구를 해킹하여 대체해 쓸 수 있습니다. 특정 통합 개발 환경 (IDE) 이나 상용 C++ 컴파일러에 국한되어 개발하기 보다, 우리는 개발자가 원하는 어떤 것도 쓸 수 있게끔 해줍니다. 소스 저장소에 생성된 코드의 추가를 강요하는 대신, 우리는 우리의 도구를 빌드 시스템에 추가하는 것을 권장합니다 : 깔끔하고 안전하고 보다 UNIX (유닉스) 정신 답습니다.

GUIs are Dynamic

GUI 들은 동적입니다.

C++ is a standarized, powerful and elaborate general-purpose language. It's the only language that is exploited on such a wide range of software projects, spanning every kind of application from entire operating systems, database servers and high end graphics applications to common desktop applications. One of the keys to C++'s success is its scalable language design that focuses on maximum performance and minimal memory consumption whilst still maintaining ANSI C compatibility.

C++ 은 표준화되고, 강력하고 우화한 범용 언어입니다. 운영체제, 데이터베이스 서버와 고-수준 그래픽 응용프로그램에서 일반 데스크탑 응용프로그램까지 어떠한 종류의 응용프로그램을 개발할 수 있는 거의 유일한 언어입니다. C++ 의 성공 요소들 중 중요한 한가지는 최고 성능에 있으면서도 최소한의 메모리 소비에 초점을 하지만 ANSI C 표준은 지키는 확장성에 있습니다.

For all these advantages, there are some downsides. For C++, the static object model is a clear disadvantage over the dynamic messaging approach of Objective C when it comes to component-based graphical user interface programming. What's good for a high end database server or an operating system isn't necessarily the right design choice for a GUI frontend. With moc, we have turned this disadvantage into an advantage, and added the flexibility required to meet the challenge of safe and efficient graphical user interface programming.

이러한 이점들이 있지만, 몇가지 단점 또한 존재합니다. C++ 을 대상으로 하는 정적 객체 모델은 컴포넌트-기반 그래픽 유저(사용자) 인터페이스를 대상으로 할 때 동적 메시지 접근을 취하는 오브젝티브 C 에 비하면 약점이 명백하게 존재합니다. 고-성능 데이터베이스 서버나 운영체제에서는 좋은 점이 GUI 프론트엔드용으로는 올바른 선택이라고 말할 수는 없습니다. moc 를 사용하면, 우리는 이러한 약점을 강점으로 만들고, 안전하고, 효율적인 유저 인터페이스 프로그램에 요구되는 유연함을 추가할 수 있습니다.

Our approach goes far beyond anything you can do with templates. For example, we can have object properties. And we can have overloaded signals and slots, which feels natural when programming in a language where overloads are a key concept. Our signals add zero bytes to the size of a class instance, which means we can add new signals without breaking binary compatibility.

우리의 접근법은 당신이 템플릿으로 할 수 있는 이상의 수준으로 진행합니다. 예를 들면, 우리는 객체 속성을 가질 수 있습니다. 그리고 오버로드(중첩)된 시그널과 슬롯을 가질수 있어서, 오버로드가 중요한 개념이 되는 곳에서 언어를 작성할 때 자연스러움을 느끼게 해줍니다. 우리의 시그널은 0 바이트에서 클래스의 인스턴스 크기까지 추가할 수 있는데, 이는 바이너리(이진) 호환성을 깨지 않으면서 새로운 시그널을 추가할 수 있습니다.

Another benefit is that we can explore an object's signals and slots at runtime. We can establish connections using type-safe call-by-name, without having to know the exact types of the objects we are connecting. This is impossible with a template based solution. This kind of runtime introspection opens up new possibilities, for example GUIs that are generated and connected from Qt Designer's XML UI files.

또다른 잇점으로 객체의 시그널과 슬롯을 실행-중(런-타임)에 탐색할 수 있다는 것입니다. 우리는 실제로 연결되는 객체의 정확한 형을 모르고 있어도, 자료형에-안전한 이름으로-호출하는 방식으로 연결을 성립시킬 수 있습니다. 이러한 실행-중 검사 방법은 새로운 가능성을 제공해주는데, 예를 들어 Qt 디자이너의 XML UI 파일을 통해 GUI 가 생성되고 연결 되게 해줍니다.

Calling Performance is Not Everything

성능을 말하는 것은 전부가 아닙니다

Qt's signals and slots implementation is not as fast as a template-based solution. While emitting a signal is approximately the cost of four ordinary function calls with common template implementations, Qt requires effort comparable to about ten function calls. This is not surprising since the Qt mechanism includes a generic marshaller, introspection, queued calls between different threads, and ultimately scriptability. It does not rely on excessive inlining and code expansion and it provides unmatched runtime safety. Qt's iterators are safe while those of faster template-based systems are not. Even during the process of emitting a signal to several receivers, those receivers can be deleted safely without your program crashing. Without this safety, your application would eventually crash with a difficult to debug free'd memory read or write error.

Qt 의 시그널과 슬롯 구현은 템플릿-기반 구현 방법만큼 빠르지 않습니다. 시그널을 발생시키는데 일반적인 템플릿 구현으로는 약 4회의 함수 호출 비용이 소비하지만, Qt 는 10회의 함수 호출이 요구됩니다. 이는 Qt 메커니즘이 제네릭 마샬러, 검증, 다른 스레드간 대기중인 호출, 그리고 궁극적인 상호일방성(일방적이면서도 상호적인 부분을 모두 포함) 을 포함하고 있어서 놀랄만한 일은 아닙니다. 그것(Qt 의 호출) 은 과도한 인-라인 구문과 코드 확장에 의존하지 않으며, 다른 것과 비교할 수 없는 실행-중(런타임) 안정성을 제공합니다. Qt 의 반복자는 빠른 템플릿-기반 시스템에서는 제공하지 않는 안정성을 가지고 있습니다. 심지어 하나의 시그널로 다수의 수신자에게 전달하는 과정 중에도, 수신자는 당신의 프로그램이 충돌 나지 않고 안전하게 제거될 수 있습니다. 이러한 안정성이 없이, 당신의 응용프로그램은 반환된 메모리의 읽기나 쓰기 오류를 디버그 하는데 어려움이 있는 크래쉬(문제있는 종료) 를 결국 얻게 될 것입니다.

Nonetheless, couldn't a template-based solution improve the performance of an application using signals and slots? While it is true that Qt adds a small overhead to the cost of calling a slot through a signal, the cost of the call is only a small proportion of the entire cost of a slot. Benchmarking against Qt's signals and slots system is typically done with empty slots. As soon as you do anything useful in your slots, for example a few simple string operations, the calling overhead becomes negligible. Qt's system is so optimized that anything that requires operator new or delete (for example, string operations or inserting/removing something from a template container) is significantly more expensive than emitting a signal.

그럼에도 불구하고, 템플릿-기반 솔루션은 시그널과 솔루션의 성능에 향상 시킬수 있지 않나요? Qt 가 시그널을 통해 슬롯을 만드는 데 약간의 비용을 추가 되는 것이 사실이지만, 이런 호출이 전체 슬롯을 만드는데 차지하는 것과 비교하면 작은 부분에 불과합니다. Qt 의 시그널과 슬롯 시스템에 대해 벤치마킹 (비교분석) 시, 일반적으로 빈 슬롯을 대상으로 수행합니다. 당신의 슬롯을 가지고, 예를 들어 단순 문자열 동작과 같은 유용한 동작을 하면, 그 때 바로 나오는 호출 비용은 무시할만한 수준입니다. Qt 의 시스템은 꽤 최적화 되어 있으므로 new 또는 delete 연산자를 요구하는 어떤 것 (예, 문자열 연산 또는 템플릿 컨테이너에서 무엇긴가를 넣거나 빼는 동작) 도 시그널을 발생시키는 것보다 비용이 훨씬 더 들어가는 걸로 보입니다.

Aside: If you have a signals and slots connection in a tight inner loop of a performance critical task and you identify this connection as the bottleneck, think about using the standard listener-interface pattern rather than signals and slots. In cases where this occurs, you probably only require a 1:1 connection anyway. For example, if you have an object that downloads data from the network, it's a perfectly sensible design to use a signal to indicate that the requested data arrived. But if you need to send out every single byte one by one to a consumer, use a listener interface rather than signals and slots.

여담: 만약 당신이 성능이 중요한 태스크에서 빠듯한 내부 루프 (반복) 에서의 시그널과 슬롯 연결이 병목 (성능저하의 요소) 이라고 결정 되었다면, 시그널과 슬롯 보다는 표준 리스너(청취자)-인터페이스 를 생각해보시기 바랍니다. 그런 곳 (병목현상) 이 있다면, 당신은 단순히 1:1 연결만을 요청해야만 할 것입니다. 예를 들어 네트워크에서 데이터를 다운로드하는 객체를 가지고 있다면, 요청된 자료가 도착했음을 알리는 데 시그널을 사용하는 것이 합리적인 설계입니다. 하지만, 소비자에게 매번 단일 바이트가 왔음을 전달할 필요가 있다면 시그널과 슬롯이 아닌, 리스너 인터페이스를 사용하세요.

No Limits

제한 없음

Because we had the moc for signals and slots, we could add other useful things to it that could not be done with templates. Among these are scoped translations via a generated tr() function, and an advanced property system with introspection and extended runtime type information. The property system alone is a great advantage: a powerful and generic user interface design tool like Qt Designer would be a lot harder to write - if not impossible - without a powerful and introspective property system. But it does not end here. We also provide a dynamic qobject_cast<T>() mechanism that does not rely on the system's RTTI and thus does not share its limitations. We use it to safely query interfaces from dynamically loaded components. Another application domain are dynamic meta objects. We can e.g. take ActiveX components and at runtime create a meta object around it. Or we can export Qt components as ActiveX components by exporting its meta object. You cannot do either of these things with templates.

시그널과 슬롯 을 위한 moc 를 가지고 있기 때문에, 우리는 템플릿으로 할 수 없는 유용한 다른 것들을 할 수 있습니다. 그들 중에는 생성된 tr() 함수를 사용하여 영역이 정해진 번역 작업과 검사 동작을 가진 고급 속성값 시스템, 확정된 실행중(런타임) 형 정보가 있습니다. 속성값 시스템 만으로도 큰 잇점이 됩니다 : - 만약 강력하고 검증할 수 있는 속성값 시스템이 없이는 Qt 디자이너와 같은 강하고 일반적인 사용자 인터페이스 디자인 툴로 코드 작성하기가 아주 어려울 수 있습니다. 하지만 그것이 끝이 아닙니다. 우리는 동적 qobject_cast<T>() 메커니즘을을 제공하여 RTTI 에 의존하지 않으며, 그로 인한 제약사항 또한 공유되지 않습니다. 우리는 동적으로 읽어진 컴포넌트들에서 안전하게 인터페이스를 질의 (문의) 할 수 있습니다. 우리는 예를 들어 ActiveX 컴포넌트들을 가져와서 실행-중에 메타 오브젝트를 만들 수 있습니다. 아니면, Qt 컴포넌트를 그것의 메타 오브젝트를 내보내는 방법을 써서 ActiveX 컴포넌트로 내보낼 수 있습니다. 당신은 이러한 것들을 템플릿을 써서는 할 수가 없습니다.

C++ with the moc essentially gives us the flexibility of Objective-C or of a Java Runtime Environment, while maintaining C++'s unique performance and scalability advantages. It is what makes Qt the flexible and comfortable tool we have today.

moc 를  포함한 C++ 은 우리에게 오브젝티브-C 나 자바 런타임 환경에서의 유연함을 제공하면서도 C++ 만의 고유한 성능과 확장성을 이점으로 가지게 해줍니다. 이것이 오늘날 우리가 가진 Qt 를 유연하고 편한안 도구로 만들게 해줍니다.

© 2015 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.