Skip to content

Commit e30646e

Browse files
authored
cxx-qt-lib: Add bindings for QMessageLogContext and qt_message_output (#814)
* cxx-qt-lib: Add bindings for QMessageLogContext and qt_message_output This allows Rust and CXX-Qt applications to send messages to the Qt logger. * cxx-qt-lib: Add const_assert for asserting at compile-time This is needed to ensure the Rust-side QMessageLogContext struct has the expected size.
1 parent e3964d4 commit e30646e

File tree

8 files changed

+256
-0
lines changed

8 files changed

+256
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3030
- CXX-Qt-build: Allow forcing initialization of crates/QML modules (`cxx_qt::init_crate!`/`cxx_qt::init_qml_module!`)
3131
- Add pure virtual function specified through the `#[cxx_pure]` attribute
3232
- Add wrappers for up and down casting, for all types which inherit from QObject, available for &T, &mut T and Pin<&mut T>
33+
- Support for `QMessageLogContext` and sending log messages to the Qt message handler.
3334

3435
### Fixed
3536

crates/cxx-qt-lib/build.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ fn main() {
179179
"core/qstringlist",
180180
"core/qt",
181181
"core/qtime",
182+
"core/qtlogging",
182183
"core/qtypes",
183184
"core/qurl",
184185
"core/quuid",
@@ -307,6 +308,7 @@ fn main() {
307308
"core/qstring",
308309
"core/qstringlist",
309310
"core/qtime",
311+
"core/qtlogging",
310312
"core/qtypes",
311313
"core/qurl",
312314
"core/quuid",
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// clang-format off
2+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
3+
// clang-format on
4+
// SPDX-FileContributor: Joshua Goins <[email protected]>
5+
//
6+
// SPDX-License-Identifier: MIT OR Apache-2.0
7+
#pragma once
8+
9+
#include "rust/cxx.h"
10+
#include <QDebug>
11+
#include <QtCore/qlogging.h>
12+
13+
QMessageLogContext
14+
construct_qmessagelogcontext(const char* fileName,
15+
int lineNumber,
16+
const char* functionName,
17+
const char* categoryName);
18+
19+
int
20+
qmessagelogcontext_line(const QMessageLogContext& context);
21+
22+
const char*
23+
qmessagelogcontext_file(const QMessageLogContext& context);
24+
25+
const char*
26+
qmessagelogcontext_function(const QMessageLogContext& context);
27+
28+
const char*
29+
qmessagelogcontext_category(const QMessageLogContext& context);
30+
31+
// Define namespace otherwise we hit a GCC bug
32+
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
33+
namespace rust {
34+
35+
template<>
36+
struct IsRelocatable<QMessageLogContext> : ::std::true_type
37+
{};
38+
39+
} // namespace rust

crates/cxx-qt-lib/src/core/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ pub use qvariant::{QVariant, QVariantValue};
109109
mod qvector;
110110
pub use qvector::{QVector, QVectorElement};
111111

112+
mod qtlogging;
113+
pub use qtlogging::{qt_message_output, QMessageLogContext, QtMsgType};
114+
112115
#[cxx::bridge]
113116
mod ffi {
114117
#[namespace = "rust::cxxqtlib1"]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// clang-format off
2+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
3+
// clang-format on
4+
// SPDX-FileContributor: Joshua Goins <[email protected]>
5+
//
6+
// SPDX-License-Identifier: MIT OR Apache-2.0
7+
#include "cxx-qt-lib/qtlogging.h"
8+
9+
#include <cxx-qt-lib/assertion_utils.h>
10+
11+
// QMessageLogContext has three "const char*" members for line, category, etc
12+
// https://codebrowser.dev/qt5/qtbase/src/corelib/global/qlogging.h.html#QMessageLogContext
13+
assert_alignment_and_size(QMessageLogContext, {
14+
int version;
15+
int line;
16+
const char* file;
17+
const char* function;
18+
const char* category;
19+
});
20+
21+
static_assert(!::std::is_trivially_copy_assignable<QMessageLogContext>::value);
22+
static_assert(
23+
!::std::is_trivially_copy_constructible<QMessageLogContext>::value);
24+
static_assert(::std::is_trivially_destructible<QMessageLogContext>::value);
25+
26+
QMessageLogContext
27+
construct_qmessagelogcontext(const char* fileName,
28+
int lineNumber,
29+
const char* functionName,
30+
const char* categoryName)
31+
{
32+
return QMessageLogContext(fileName, lineNumber, functionName, categoryName);
33+
}
34+
35+
int
36+
qmessagelogcontext_line(const QMessageLogContext& context)
37+
{
38+
return context.line;
39+
}
40+
41+
const char*
42+
qmessagelogcontext_file(const QMessageLogContext& context)
43+
{
44+
return context.file;
45+
}
46+
47+
const char*
48+
qmessagelogcontext_function(const QMessageLogContext& context)
49+
{
50+
return context.function;
51+
}
52+
53+
const char*
54+
qmessagelogcontext_category(const QMessageLogContext& context)
55+
{
56+
return context.category;
57+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
2+
// SPDX-FileContributor: Joshua Goins <[email protected]>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
use cxx::{type_id, ExternType};
6+
use std::ffi::c_char;
7+
use std::ffi::CStr;
8+
use std::marker::PhantomData;
9+
use std::mem::size_of;
10+
11+
#[cxx::bridge]
12+
mod ffi {
13+
/// The level the message is sent to the message handler at.
14+
#[repr(i32)]
15+
enum QtMsgType {
16+
/// A debug message.
17+
QtDebugMsg = 0,
18+
/// An info message.
19+
QtInfoMsg = 4,
20+
/// A warning message.
21+
QtWarningMsg = 1,
22+
/// A fatal message.
23+
QtFatalMsg = 3,
24+
/// A critical message.
25+
QtCriticalMsg = 2,
26+
}
27+
28+
unsafe extern "C++" {
29+
include!("cxx-qt-lib/qstring.h");
30+
type QString = crate::QString;
31+
32+
include!("cxx-qt-lib/qtlogging.h");
33+
type QMessageLogContext<'a> = crate::QMessageLogContext<'a>;
34+
type QtMsgType;
35+
36+
/// Outputs a message in the Qt message handler.
37+
fn qt_message_output(msgType: QtMsgType, context: &QMessageLogContext, string: &QString);
38+
39+
#[cxx_name = "qmessagelogcontext_line"]
40+
#[doc(hidden)]
41+
fn line(context: &QMessageLogContext) -> i32;
42+
43+
#[cxx_name = "qmessagelogcontext_file"]
44+
#[doc(hidden)]
45+
unsafe fn file(context: &QMessageLogContext) -> *const c_char;
46+
47+
#[cxx_name = "qmessagelogcontext_function"]
48+
#[doc(hidden)]
49+
unsafe fn function(context: &QMessageLogContext) -> *const c_char;
50+
51+
#[cxx_name = "qmessagelogcontext_category"]
52+
#[doc(hidden)]
53+
unsafe fn category(context: &QMessageLogContext) -> *const c_char;
54+
}
55+
56+
#[namespace = "rust::cxxqtlib1"]
57+
unsafe extern "C++" {
58+
include!("cxx-qt-lib/common.h");
59+
60+
#[doc(hidden)]
61+
#[rust_name = "construct_qmessagelogcontext"]
62+
unsafe fn construct<'a>(
63+
file_name: *const c_char,
64+
line_number: i32,
65+
function_name: *const c_char,
66+
category_name: *const c_char,
67+
) -> QMessageLogContext<'a>;
68+
}
69+
}
70+
71+
/// The QMessageLogContext struct defines the context passed to the Qt message handler.
72+
#[repr(C)]
73+
#[derive(Clone, Copy)]
74+
pub struct QMessageLogContext<'a> {
75+
version: i32,
76+
line: i32,
77+
file: *const c_char,
78+
function: *const c_char,
79+
category: *const c_char,
80+
_phantom: PhantomData<&'a c_char>,
81+
}
82+
83+
const_assert!(
84+
size_of::<QMessageLogContext>() == (size_of::<i32>() * 2) + (size_of::<*const c_char>() * 3)
85+
);
86+
87+
impl<'a> QMessageLogContext<'a> {
88+
pub fn new(
89+
file: &'a CStr,
90+
line: i32,
91+
function: &'a CStr,
92+
category: &'a CStr,
93+
) -> QMessageLogContext<'a> {
94+
unsafe {
95+
ffi::construct_qmessagelogcontext(
96+
file.as_ptr(),
97+
line,
98+
function.as_ptr(),
99+
category.as_ptr(),
100+
)
101+
}
102+
}
103+
104+
/// The line number given to the message handler.
105+
pub fn line(&self) -> i32 {
106+
ffi::line(self)
107+
}
108+
109+
/// The file path given to the message handler.
110+
pub fn file(&self) -> &'a CStr {
111+
unsafe { CStr::from_ptr(ffi::file(self)) }
112+
}
113+
114+
/// The name of the function given to the message handler.
115+
pub fn function(&self) -> &'a CStr {
116+
unsafe { CStr::from_ptr(ffi::function(self)) }
117+
}
118+
119+
/// The category given to the message handler.
120+
pub fn category(&self) -> &'a CStr {
121+
unsafe { CStr::from_ptr(ffi::category(self)) }
122+
}
123+
}
124+
125+
// Safety:
126+
//
127+
// Static checks on the C++ side ensure that QMessageLogContext is trivial.
128+
unsafe impl ExternType for QMessageLogContext<'_> {
129+
type Id = type_id!("QMessageLogContext");
130+
type Kind = cxx::kind::Trivial;
131+
}
132+
133+
use crate::const_assert;
134+
pub use ffi::{qt_message_output, QtMsgType};

crates/cxx-qt-lib/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ pub use crate::qml::*;
2525
mod quickcontrols;
2626
#[cfg(feature = "qt_quickcontrols")]
2727
pub use crate::quickcontrols::*;
28+
29+
mod util;

crates/cxx-qt-lib/src/util.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
2+
// SPDX-FileContributor: Joshua Goins <[email protected]>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
/// Asserts that a boolean expression is true at compile time.
7+
///
8+
/// See [`core::assert!`] for more information.
9+
///
10+
/// ```compile_fail
11+
/// const_assert!(5 == 4);
12+
/// ```
13+
#[macro_export]
14+
macro_rules! const_assert {
15+
($x:expr $(,)?) => {
16+
const _: () = ::core::assert!($x);
17+
};
18+
}

0 commit comments

Comments
 (0)