Meta-Object Compiler (moc) 사용
Meta-Object Compiler, moc
은 Qt의 C++ 확장을 처리하는 프로그램입니다.
moc
도구는 C++ 헤더 파일을 읽습니다. Q_OBJECT 매크로가 포함된 클래스 선언을 하나 이상 발견하면 해당 클래스의 메타 객체 코드가 포함된 C++ 소스 파일을 생성합니다. 무엇보다도 시그널 및 슬롯 메커니즘, 런타임 유형 정보, 동적 속성 시스템에는 메타 객체 코드가 필요합니다.
moc
에서 생성된 C++ 소스 파일을 컴파일하고 클래스 구현과 연결해야 합니다.
qmake와 CMake는 모두 moc
을 호출하는 빌드 규칙이 포함된 메이크파일을 생성하므로 moc
을 직접 사용할 필요가 없습니다. qmake
은 기본적으로 이러한 빌드 규칙을 추가하는 반면, CMake를 사용하면 AUTOMOC 속성을 사용하여 moc
을 자동으로 처리할 수 있습니다. moc
에 대한 자세한 배경 정보는 Qt가 신호와 슬롯에 Moc을 사용하는 이유 를 참조하십시오 .
사용법
moc
는 일반적으로 이와 같은 클래스 선언이 포함된 입력 파일과 함께 사용됩니다:
class MyClass : public QObject { Q_OBJECT public: MyClass(QObject *parent = 0); ~MyClass(); signals: void mySignal(); public slots: void mySlot(); };
moc
는 위에 표시된 신호와 슬롯 외에도 다음 예제에서와 같이 객체 프로퍼티도 구현합니다. Q_PROPERTY () 매크로는 객체 프로퍼티를 선언하고 Q_ENUM()는 프로퍼티 시스템 내에서 사용할 수 있는 클래스 내 열거형 목록을 선언합니다.
다음 예에서는 priority
이라고도 하는 열거 유형 Priority
의 프로퍼티를 선언하고 priority()
함수와 setPriority()
함수를 갖습니다.
class MyClass : public QObject { Q_OBJECT Q_PROPERTY(Priority priority READ priority WRITE setPriority) public: enum Priority { High, Low, VeryHigh, VeryLow }; Q_ENUM(Priority) MyClass(QObject *parent = 0); ~MyClass(); void setPriority(Priority priority) { m_priority = priority; } Priority priority() const { return m_priority; } private: Priority m_priority; };
Q_FLAG() 매크로는 플래그, 즉 OR로 함께 사용할 열거형을 선언합니다. 또 다른 매크로인 Q_CLASSINFO()를 사용하면 클래스의 메타 객체에 추가 이름/값 쌍을 첨부할 수 있습니다:
class MyClass : public QObject { Q_OBJECT Q_CLASSINFO("Author", "Oscar Peterson") Q_CLASSINFO("Status", "Active") public: MyClass(QObject *parent = 0); ~MyClass(); };
moc
에서 생성된 출력은 프로그램의 다른 C++ 코드와 마찬가지로 컴파일 및 링크되어야 하며, 그렇지 않으면 최종 링크 단계에서 빌드가 실패합니다. qmake
을 사용하는 경우 이 작업은 자동으로 수행됩니다. qmake
이 실행될 때마다 프로젝트의 헤더 파일을 구문 분석하고 Q_OBJECT 매크로가 포함된 파일에 대해 moc
을 호출하는 make 규칙을 생성합니다. 마찬가지로 AUTOMOC를 ON
로 설정하면 CMake는 빌드 시 헤더 및 소스 파일을 검사하고 그에 따라 moc
을 호출합니다.
클래스 선언이 myclass.h
파일에서 발견되면 moc 출력은 moc_myclass.cpp
이라는 파일에 넣어야 합니다. 그런 다음 이 파일을 평소와 같이 컴파일하여 객체 파일(예: Windows의 경우 moc_myclass.obj
)을 생성해야 합니다. 그런 다음 이 객체는 프로그램의 최종 빌드 단계에서 함께 링크되는 객체 파일 목록에 포함되어야 합니다.
호출을 위한 Make 규칙 작성 moc
가장 간단한 테스트 프로그램을 제외한 모든 프로그램의 경우 moc
. 프로그램의 메이크 파일에 몇 가지 규칙을 추가하면 make
이 필요할 때 moc을 실행하고 moc 출력을 처리할 수 있도록 자동화하는 것이 좋습니다.
CMake 또는 qmake를 사용하여 필요한 모든 moc
처리를 수행하는 메이크파일을 생성할 수 있습니다.
메이크파일을 직접 생성하려는 경우 다음은 moc 처리를 포함하는 방법에 대한 몇 가지 팁입니다.
헤더 파일에서 Q_OBJECT 클래스 선언의 경우 GNU make만 사용하는 경우 유용한 메이크파일 규칙은 다음과 같습니다:
moc_%.cpp: %.h moc $(DEFINES) $(INCPATH) $< -o $@
이식 가능하게 작성하려면 다음 형식의 개별 규칙을 사용할 수 있습니다:
moc_foo.cpp: foo.h moc $(DEFINES) $(INCPATH) $< -o $@
SOURCES
(원하는 이름으로 대체) 변수에 moc_foo.cpp
, moc_foo.o
또는 moc_foo.obj
변수에 OBJECTS
을 추가하는 것도 잊지 마세요.
두 예제 모두 $(DEFINES)
및 $(INCPATH)
이 정의로 확장되어 C++ 컴파일러에 전달되는 경로 옵션을 포함한다고 가정합니다. 이러한 옵션은 moc
에서 소스 파일을 전처리하는 데 필요합니다.
C++ 소스 파일의 이름은 .cpp
을 선호하지만 원하는 경우 .C
, .cc
, .CC
, .cxx
, .c++
과 같은 다른 확장자를 사용할 수 있습니다.
구현(.cpp
) 파일의 Q_OBJECT 클래스 선언의 경우 다음과 같은 makefile 규칙을 사용하는 것이 좋습니다:
foo.o: foo.moc foo.moc: foo.cpp moc $(DEFINES) $(INCPATH) -i $< -o $@
이렇게 하면 make가 foo.cpp
을 컴파일하기 전에 moc을 실행하도록 보장합니다. 그런 다음 의 끝에
#include "foo.moc"
를 foo.cpp
의 끝에 추가하면 해당 파일에 선언된 모든 클래스를 완전히 알 수 있습니다.
명령줄 옵션
다음은 moc에서 지원하는 명령줄 옵션입니다:
옵션 | 설명 |
---|---|
-D<macro>[=<def>] | 선택적 정의로 매크로를 정의합니다. |
-E | 전처리 전용이며 메타 객체 코드를 생성하지 않습니다. |
-f[<file>] | 출력에 #include 문을 강제로 생성합니다. 확장자가 H 또는 h 으로 시작하는 헤더 파일의 기본값입니다. 이 옵션은 표준 명명 규칙을 따르지 않는 헤더 파일이 있는 경우에 유용합니다. <file> 부분은 선택 사항입니다. |
-Fdir | macOS. 헤더 파일을 검색할 디렉터리 목록의 맨 위에 프레임워크 디렉터리 dir 를 추가합니다. 이 디렉터리는 -I 옵션으로 지정한 디렉터리와 인터리빙되며 왼쪽에서 오른쪽 순서로 검색됩니다(gcc에 대한 매뉴얼 페이지 참조). 일반적으로 -F /라이브러리/프레임워크/를 사용합니다. |
-h | 사용법과 옵션 목록을 표시합니다. |
-i | 출력에 #include 문을 생성하지 마세요. 이 명령은 하나 이상의 클래스 선언이 포함된 C++ 파일에서 moc을 실행하는 데 사용할 수 있습니다. 그런 다음 .cpp 파일에서 메타 객체 코드를 #include 해야 합니다. |
-I<dir> | 헤더 파일의 포함 경로에 dir을 추가합니다. |
-M<key=value> | 플러그인에 메타 데이터를 추가합니다. 클래스에 Q_PLUGIN_METADATA 가 지정된 경우 키-값 쌍이 메타 데이터에 추가됩니다. 이는 런타임에 플러그인에 대해 확인되는 Json 객체에 포함됩니다( QPluginLoader 에서 액세스 가능). 이 인수는 일반적으로 빌드 시스템에서 확인된 정보로 정적 플러그인에 태그를 지정하는 데 사용됩니다. |
-nw | 경고를 생성하지 않습니다. (권장하지 않습니다.) |
-o<file> | 표준 출력 대신 <file> 에 출력을 씁니다. |
-p<path> | 생성된 #include 문에서 파일 이름 앞에 <path>/ 을 추가합니다. |
-U<macro> | 매크로 정의 해제. |
@<file> | <file> 에서 추가 명령줄 옵션을 읽습니다. 파일의 각 줄은 하나의 옵션으로 취급됩니다. 빈 줄은 무시됩니다. 이 옵션은 옵션 파일 자체 내에서 지원되지 않습니다(즉, 옵션 파일은 다른 파일을 "포함"할 수 없습니다). |
-v | moc 의 버전 번호를 표시합니다. |
moc
는 전처리기 기호 Q_MOC_RUN
를 정의합니다. 로 둘러싸인 모든 코드는
#ifndef Q_MOC_RUN ... #endif
로 둘러싸인 코드는 moc
로 건너뜁니다.
진단
moc
는 Q_OBJECT 클래스 선언에 있는 여러 가지 위험하거나 불법적인 구조에 대해 경고합니다.
프로그램의 최종 빌드 단계에서 YourClass::className()
가 정의되지 않았거나 YourClass
에 vtable이 없다는 연결 오류가 발생하면 뭔가 잘못되었다는 뜻입니다. 대부분의 경우 moc로 생성된 C++ 코드를 컴파일하거나 #include
컴파일하는 것을 잊었거나(전자의 경우) 링크 명령에 해당 객체 파일을 포함하는 것을 잊어버렸기 때문입니다. qmake
을 사용하는 경우 다시 실행하여 메이크파일을 업데이트해 보세요. 이렇게 하면 문제가 해결됩니다.
시스템 빌드
헤더 moc 파일 포함
헤더 moc 파일 포함과 관련하여 qmake와 CMake는 다르게 작동합니다.
예제를 통해 이를 설명하기 위해 해당 소스 파일이 있는 헤더가 두 개 있다고 가정해 보겠습니다: a.h
, a.cpp
, b.h
, b.cpp
입니다. 각 헤더에는 Q_OBJECT 매크로가 있습니다:
// a.h class A : public QObject { Q_OBJECT public: // ... };
// a.cpp #include "a.h" // ... #include "moc_a.cpp"
// b.h class B : public QObject { Q_OBJECT public: // ... };
// b.cpp #include "b.h" // ... #include "moc_b.cpp"
qmake를 사용하면 moc 생성 파일(moc_a.cpp
/moc_b.cpp
)을 포함하지 않으면 a.cpp
, b.cpp
, moc_a.cpp
, moc_b.cpp
이 개별적으로 컴파일됩니다. 이로 인해 빌드 속도가 느려질 수 있습니다. moc 생성 파일을 포함하면 moc 생성 코드가 해당 파일에 포함되어 있으므로 a.cpp 및 b.cpp만 컴파일하면 됩니다.
CMake를 사용하면 파일을 포함하지 않으면 moc에 의해 하나의 추가 파일이 생성됩니다(예제에서는 cmake.cpp
이라고 하겠습니다). cmake.cpp
에는 moc_a.cpp
와 moc_b.cpp
이 모두 포함됩니다. moc 생성 파일을 포함할 수도 있지만 반드시 포함해야 하는 것은 아닙니다.
이 주제와 관련된 CMake의 moc 지원에 대한 자세한 내용은 소스에 헤더 moc 파일 포함하기를 참조하세요.
제한 사항
moc
는 모든 C++를 처리하지 않습니다. 가장 큰 문제는 클래스 템플릿에 Q_OBJECT 매크로를 가질 수 없다는 것입니다. 다음은 예시입니다:
class SomeTemplate<int> : public QFrame { Q_OBJECT ... signals: void mySignal(int); };
다음 구조는 불법입니다. 이들 모두는 일반적으로 더 나은 대안이 있으므로 이러한 제한을 제거하는 것은 우선순위가 높지 않습니다.
다중 상속 시 QObject가 먼저 있어야 함
다중 상속을 사용하는 경우 moc
은 첫 번째 상속된 클래스가 QObject 의 하위 클래스라고 가정합니다. 또한 첫 번째 상속된 클래스만 QObject 인지 확인하십시오.
// correct class SomeClass : public QObject, public OtherClass { ... };
QObject 를 사용한 가상 상속은 지원되지 않습니다.
함수 포인터는 신호 또는 슬롯 매개변수가 될 수 없음
함수 포인터를 신호 또는 슬롯 파라미터로 사용하려는 대부분의 경우 상속이 더 나은 대안이라고 생각합니다. 다음은 불법 구문의 예시입니다:
class SomeClass : public QObject { Q_OBJECT public slots: void apply(void (*apply)(List *, void *), char *); // WRONG };
다음과 같이 이 제한을 해결할 수 있습니다:
typedef void (*ApplyFunction)(List *, void *); class SomeClass : public QObject { Q_OBJECT public slots: void apply(ApplyFunction, char *); };
함수 포인터를 상속 및 가상 함수로 대체하는 것이 더 나은 경우도 있습니다.
열거형과 타입 정의는 신호 및 슬롯 매개변수에 대해 완전한 자격을 갖춰야 합니다.
인수의 서명을 확인할 때 QObject::connect()는 데이터 유형을 문자 그대로 비교합니다. 따라서 Alignment 과 Qt::Alignment 은 서로 다른 두 가지 유형으로 취급됩니다. 이 제한을 해결하려면 신호와 슬롯을 선언할 때와 연결을 설정할 때 데이터 유형을 완전히 한정해야 합니다. 예를 들어
class MyClass : public QObject { Q_OBJECT enum Error { ConnectionRefused, RemoteHostClosed, UnknownError }; signals: void stateChanged(MyClass::Error error); };
중첩된 클래스는 신호 또는 슬롯을 가질 수 없음
다음은 문제가 되는 구조의 예시입니다:
class A { public: class B { Q_OBJECT public slots: // WRONG void b(); }; };
신호/슬롯 반환 유형은 참조가 될 수 없음
신호와 슬롯은 반환 유형을 가질 수 있지만 참조를 반환하는 신호나 슬롯은 무효를 반환하는 것으로 취급됩니다.
클래스의 signals
및 slots
섹션에는 시그널과 슬롯만 나타날 수 있습니다.
moc
클래스의 signals
또는 slots
섹션에 신호와 슬롯이 아닌 다른 구조체를 넣으려고 하면 컴플레인이 발생합니다.
메타 객체 시스템, 시그널과 슬롯, Qt의 프로퍼티 시스템도참조하십시오 .
© 2025 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.