Skip to content

Monday, 17 June 2024

People of the Internet,

While working on keystroke events, I realized my improvements to the event.change property were still inconsistent for certain Unicode characters. This led me to delve into code units, code points, graphemes, and other cool Unicode concepts. I found this blog post to be very enlightening.

Here’s an update on my progress over the past two weeks:

MRs merged:

  • event.change : The change property of the event object now correctly handles Unicode, with adjustments to selStart and selEnd calculations. !MR998
  • cursor position and undo/redo fix : Fixed cursor position calculations to account for rejected input text and resolved merging issues with undo/redo commands in text fields. !MR1011
  • DocOpen Event implementation : Enabled document-level scripts to access the event object by implementing the DocOpen event. !MR1003
  • Executing validation events correctly : Fixed a bug where validation scripts wouldn’t run after KeystrokeCommit scripts in Okular. !MR999
  • Widget refresh functions for RadioButton, ListEdit and ComboEdit : Added refresh functions as slots for RadioButton, ListEdit, and ComboEdit widgets, aiding in reset functionality and script updates. !MR1012
  • Additional document actions in Poppler : Implemented reading additional document actions (CloseDocument, SaveDocumentStart, SaveDocumentFinish, PrintDocumentStart, PrintDocumentFinish) in the qt5 and qt6 frontends for Poppler. !MR1561 (in Poppler)

MRs currently under review:

  • Reset form implementation in qt6 frontend for Okular : Working on the reset form functionality in Okular, currently focusing on qt6 frontend details. !MR1564 (in Poppler)
  • Reset form in Okular : Using the Poppler API to reset forms within Okular. !MR1007
  • Fixing order of execution of events for text form fields : Addressing the incorrect execution order of certain events (e.g., calculation scripts) and ensuring keystroke commit, validation, and format scripts are evaluated correctly when setting fields via JavaScript. !MR1002

For the coming weeks, my focus will be on implementing reset forms, enhancing keystroking and formatting scripts, and possibly starting on submit forms. Let’s see how it goes.

See you next time. Cheers!

Sunday, 16 June 2024

This year again I participated to the KDE PIM Sprint in Toulouse. As always it was really great to meet other KDE contributors and to work together for one weekend. And as you might have seen on my Mastodon account, a lot of food was also involved.

Day 1 (Friday Afternoon)

We started our sprint on Thursday with a lunch at the legendary cake place, which I missed last year due to my late arrival.

Picture of some delicious cakes: a piece of cheesecake raspberry and basil, a piece of lemon tart with meringue and a piece of carrot cake)
Picture of some delicious cakes: a piece of cheesecake raspberry and basil, a piece of lemon tart with meringue and a piece of carrot cake)

We then went to the coworking space where we would spend the remaining of this sprint and started working on defining tasks to work on and putting them on real kanban board.

A kanban board with tasks to discuss and to implement
A kanban board with tasks to discuss and to implement

To get a good summary of the specific topics we discussed, I invite you to consult the blog of Kevin.

That day, aside from the high level discussion, I proceeded to port away the IMAP authentification mechanism for Outlook accounts away from the KWallet API to use the more generic QtKeychain API. I also removed a large dependency from libkleo (the KDE library to interact with GPG).

Day 2 (Saturday)

On the second day, we were greated by a wonderful breakfast (thanks Kevin).

Picture of croissant, brioche and chocolatine
Picture of croissant, brioche and chocolatine

I worked on moving EventViews (the library that renders the calendar in KOrganizer) and IncidenceEditor (the library that provides the event/todo editor in KOrganizer) to KOrganizer. This will allow to reduce the number of libraries in PIM.

For lunch, we ended up eating at the excellent Mexican restaurant next to the location of the previous sprint.

Mexican food
Mexican food

I also worked on removing the “Add note” functionality in KMail. This feature allow to store notes to emails following RFC5257. Unfortunatelty this RFC never left the EXPERIMENTAL state and so these notes were only stored in Akonadi and not synchronized with any services.

This allow to remove the relevant widget from the pimcommon library and the Akonadi attribute.

I also started removing another type of notes: the KNotes app which provided sticky notes. This application was not maintained anymore, didn’t work so well with Wayland. If you were using KNotes, to make sure you don’t loose your notes, I added support in Marknote to import notes from KNotes.

Marknote with the context menu to import notes
Marknote with the context menu to import notes

Finally I worked on removing visible Akonadi branding from some KDE PIM applications. The branding was usually only visible when an issue occurred, which didn’t help with Akonadi reputation.

We ended up working quite late and ordering Pizzas. I personally got one with a lot of cheese (but no photo this time).

Day 3 (Sunday)

The final day, we didn’t had any breakfast :( but instead a wonderful brunch.

Picture of the brunch
Picture of the brunch

Aside from eating, I started writing a plugin system for the MimeTreeParser which powers the email viewer in Merkuro and in Kleopatra. In the short term, I want to be able to add Itinerary integration in Merkuro but in the longer term the goal is to bring this email viewer to feature parity with the email viewer from KMail and then replace the KMail email viewer with the one from Merkuro. Aside from removing duplicate code, this will improve the security since the individual email parts are isolated from each other and this will makes it easier for the email view to follow KDE styling as this is just normal QML instead of fake HTML components.

I also merged and rebased some WIP merge requests in Marknote in preparation of a new release soon and reviewed merge requests from the others.

Last but not least

If you want to know more or engage with us, please join the KDE PIM and the Merkuro matrix channels! Let’s chat further.

Also, I’d like to thank again Étincelle Coworking and KDE e.V. to make this event possible. This wouldn’t be possible without a venue and without at least partial support in the travel expenses.

Finally, if you like such meetings to happen in the future so that we can push forward your favorite software, please consider making a tax-deductible donation to the KDE e.V. foundation.

Friday, 14 June 2024

This is the first blog post of my GSOC journey. I will be sharing my works and experiences here. Stay tuned for more updates. In this blog, I’ll be sharing my experiences of the first two weeks of GSOC, what are the works I did, what are challenges I faced and how did I overcome them ( Did I really overcome them :P ).

On my first week I tried to understand the codebase of discover first, via doing small changes. So, the first thing I added was a new way of verification of snap publishers which is officially supported by snapcraft. Snapcraft has two tiers of verification:

 

This is the release schedule the release team agreed on

  https://community.kde.org/Schedules/KDE_Gear_24.08_Schedule

Dependency freeze is in around 4 weeks (July 18) and feature freeze one
after that. Get your stuff ready!
 

After three months, KDE's Kirigami tutorial has been ported to Qt6.

In case you are unaware of what Kirigami is:

  • Qt provides two GUI technologies to create desktop apps: QtWidgets and QtQuick
  • QtWidgets uses only C++ while QtQuick uses QML (plus optional C++ and JavaScript)
  • Kirigami is a library made by KDE that extends QtQuick and provides a lot of niceties and quality-of-life components

Strictly speaking there weren't that many API changes to Kirigami. The most notable changes were:

  • switching from Kirigami.Overlaysheet to Kirigami.Dialog
  • switching from actions.{main,left,right} to raw actions
  • switching from Kirigami.BasicListItem to Controls.ItemDelegate/Kirigami.TitleSubtitle et al.

Which are all easy-to-understand API, so developers shouldn't have issues porting to those. In any case, there is a wiki page with some instructions on porting to Kirigami with Qt6.

No, the actual challenge came from a different front: moving to declarative registration.

The old method of registering types from C++ to QML involved using a global context or specific API calls, which usually meant instantiating the object in C++ code, which can be annoying. Depending on the registration type and how it was called, it would also be necessary to implement a factory.

The new method involves creating QML modules directly from CMake and adding one or two macros in the class you want to expose, so you don't really have to instantiate anything in your C++ code. As an additional bonus, you can add QML resources directly from CMake instead of writing XML code.

The relevant Qt API for the new declarative registration is qt_add_qml_module(), but for our KDE stuff we use ecm_add_qml_module(), which removes the upstream boilerplate.

One consequence of the new declarative method of registering QML modules is that URIs are expected to use reverse DNS naming schemes (for example, org.kde.kirigami.delegates) and their resource path becomes way too long:

<resource_prefix><import_URI><file>

An example would be qrc:/qt/qml/org/kde/example/SomeModule.qml.

Which leads to an API change: from engine.load(“qrc:/qt/qml/org/kde/example/SomeModule.qml”) to engine.loadFromModule(“org.kde.example”, “SomeModule”), which is much shorter.

loadFromModule() however requires, as the name implies, a QML module, which needs to start with an uppercase character. This means every main.qml needs to turn into Main.qml. This needs to be done everywhere in the tutorial, it's a chore task.

A nice-to-have benefit of the port to Qt6 is that we no longer need to specify import versions in QML files. For API users this means less concerns with version numbering and for tutorial writers this means not needing to hunt for a specific import version. But this also meant a chore task of removing imports from everywhere.

Since this is the more evident change that indicates the new content is using Qt6, that’s what I started the tutorial port with.

Quality of life changes🔗

A long-standing issue with the Kirigami tutorial had been the lack of up-to-date screenshots. In particular:

  • screenshots that don't reflect the code
  • screenshots that had X11 window icons
  • screenshots that had Wayland window icons
  • screenshots that looked plain bad
  • screenshots that were badly positioned

Because a product is only as good as its documentation, that is to say, when the product is docs, this had paramount importance. If the product is showcased in a non-professional way, it ends up becoming unattractive.

The API change from Overlaysheet to Dialog meant a drastic UI change. Since I needed to update those screenshots, I could use the new Dialog redesign by Carl Schwann, which looks great.

The API change to Kirigami actions also reflected in UI changes, but mostly on mobile. And Plasma Mobile was under-represented in the tutorial, so it needed updates too.

So I went on about updating most screenshots, which entails a few things:

  • make the screenshots represent the actual code
  • make the actual code represent the screenshots
  • fix spacing between comparison screenshots
  • fix screenshot positioning
  • add a desktop file for the examples so window icons show up on Wayland
  • explain how to install the application so window icons show up on Wayland

The first two meant changes in app design with the same questions posed in the next section of this blog post. The last two were in fact the result of my lackluster updates from years ago.

Historically the original Kirigami tutorial had many blatant issues such as missing code, few screenshots, barely any links, no build or run instructions, etc. When I updated it years ago, I did not go full length with it and I didn’t have the expertise necessary.

In order to have window icons on Wayland, the application needs to be installed on the system and provide a desktop file. On X11 it is possible to disregard the desktop file requirement, but the application still needs to be installed. Because I didn’t know how to install applications properly, I only provided build instructions and as a consequence wasn’t able to get screenshots with window icons. The updated tutorial has those.

In addition to that, because the code is more accurate and provides its own desktop file and correct install instructions, the life of any tutorial tester gets much easier in the future, myself included. You can blindly copy+paste the code and the project will be as it should. Who would have thought that providing reproducible code and instructions helps QA and saves time in the end? /s

With the current update, unless there are drastic API or tutorial changes, most of the bits I touched should last well into Plasma 7 at least.

Documentation challenges🔗

For someone dealing with documentation, updating a tutorial on writing desktop applications isn't a small or simple task. There are a few things one needs to think about:

  • what app design should I recommend to users?
  • what app design is actually in use?
  • what app design does upstream expect of API users?
  • what API should get the spotlight?
  • have I mentioned a keyword so the reader knows what to search for?
  • how to design code examples to be didactic?
  • how to design code examples that look good in screenshots?
  • what level of explanation should I provide?
  • what can I remove?
  • when should code examples be short or complete?
  • have I presented all concepts necessary for the user to finish the tutorial?
  • what should be a part of the tutorial and what should be a note?
  • what documentation practices should I follow and not follow?
  • should I include workarounds to common issues?
  • should I include notes about possible issues?
  • if so where?
  • how should screenshots be organized in the page?
  • what sections should I use for this page?
  • in which order should the sections be read?
  • have I made this consistent across all text I touched?
  • what screenshots are missing?
  • what links are missing?
  • what should I leave for later?

These questions need to be in your mind and you need to make lasting decisions on most of them in order to progress. This naturally does not mean you should address all of them at the same time, just that you need to keep those in mind.

The app design questions were the most difficult to think about, not so much for the code examples we use, but because of project file structure, which can be extremely malleable in C++. Even just searching through KDE projects, I managed to find 5 different project structures! Most of the time going counter to upstream Qt’s recommendations. For example, followed to the letter, Qt would recommend a file structure like the URI (src/org/kde/example/SomeModule.qml) while KDE almost never does that (src/SomeModule.qml or src/qml/SomeModule.qml or src/somedirname/SomeModule.qml).

I went with a sort of hybrid approach similar to solution 2 of this Qt blog post with Main.qml in the src/ directory and other QML modules in a separate subdirectory (or multiple subdirs), which is more didactic and scales better.

Another aspect one must not succumb to is demotivation caused by chore tasks! Things like thinking about app design or figuring out didactic ways to address issues can be challenging and fun, but chore tasks can drain your soul.

For me the most draining was updating screenshots, because I ended up having to do about 200 of those, either due to mistakes or due to didactic/design changes. In the end I had updated 51 tutorial screenshots or so, and the tutorial has more than that. The Kirigami tutorial alone has 30 pages of content after all (I touched only 19 of them).

Generally, when dealing with API documentation and tutorials, the writer needs to go through all of this trouble so that the reader does not go through any trouble at all and can focus on what they need.

Learning more🔗

To learn about more questions like these and how to address them, two good resources I recommend that are free are the Google Developer Documentation Style Guide and the Google Technical Writing Course. We don’t follow them to the letter, but it is to-the-point and useful as a starting point.

We also have the KDE Develop Style Guidelines which addresses some of the more specific KDE Develop instructions. I’d like to point out the last section: Follow standard documentation practices where it makes sense, which contains a list of other resources to learn about technical writing, including some content personally curated by me.

KDE has a good entrypoint wiki page for documentation explaining the various areas of documentation you can work with at KDE and the technologies used.

Documentation aside, we also have a dedicated page full of resources to learn Qt development, including a curated list of study materials.

I have a personal repository called minimal-qml6-examples which, while unfinished/unpolished, provides lots of code examples with raw Qt to understand the essentials of QML registration using the new CMake API.

Thursday, 13 June 2024

Hi! It has been over two weeks since the coding period began. In this blog post, I will provide a brief summary of my work during the first two weeks.

After spending some time reviewing the code, I decided to start by refactoring the existing code related to ASS format subtitles. This has two main goals: first, to enable Kdenlive to read as much information as possible from ASS subtitles (specifically, the features supported by libass) and load it into the memory; and second, to ensure that Kdenlive can save all this information back to the file. Since SubtitleModel already contains a significant amount of code for ASS format subtitles, my work mainly involved refining and expanding upon this existing code while maintaining compatibility.

So far, I have accomplished the following:

  • Added initial support for reading and saving embedded fonts in ASS subtitles
  • Optimized the storage method for subtitle styles
  • Migrated from V4Style to V4+Style

Tasks still to be completed:

  • Modify m_subtitleList to accommodate more information.
  • Write unit tests for each feature

Once these steps are completed, we will have more comprehensive support for ASS format subtitles, marking the end of this phase of the ASS code refactoring. The next focus will be on refactoring the functionality for modifying subtitle styles. These two parts will be my primary focus for the next two weeks. Stay tuned!

A strange fact recently came into my purview – many users and developers don’t know what Open Source is.

Some time ago, there was a controversy regarding the Floorp web browser (a fork of Firefox) going closed source.

The cause for this was that some company forked Floorp and made a browser based on it. I’ll not comment on the irony that the author of a fork of a project complained that someone forked his project.

Obviously, this triggered a storm of negative reactions on quite a few platforms where fans of Free Software / Open Source software hang out.

The developer responded that this is just temporary, and that the browser will soon be Open Source again. After a while, the repositories became public again and all was fine.

The developer said that Floorp is again Open Source, the angry mob said good, the Floorp browser is again Open Source. And every discussion about Floorp got a plethora of comments that people should stop complaining, that Floorp is Open Source, and that the previous situation was just a misunderstanding.

Open Source

The term Open Source is well defined at opensource.org.

Making the source code of a program publicly available is not enough for something to be Open Source. Having an army of people saying that something is Open Source, is not enough for something to be Open Source.

If a license that the code is published under doesn’t conform to the criteria published on opensource.org, a program is not Open Source. A program whose license contains the following sentence, is, by definition, not Open Source:

You may not use or distribute this Software or any derivative works in any form for commercial purposes. Examples of commercial purposes would be running business operations, licensing, leasing, or selling the Software, or distributing the Software for use with commercial products.

Floorp private components/LICENSE

This is strangely worded as it looks like you can not use the Floorp web browser to access, for example, a work e-mail account as that would be using it for commercial purposes. This is likely not what the license author intended – the intent of the license is to disallow creating commercial products by forking Floorp.

While it is a valid desire of the author not to have somebody else profit from his work, it is the thing that makes the Floorp web browser not Open Source.

You can call it ‘source available’, you can call it ‘fair code’ but you can not call it Open Source.

Update: A few days after this blog was published, Floorp moved the code from the private submodule to the main repository. So, it should be again Open Source. Let’s hope it remains like that.

Redefining Open Source

This blog post should have been written when the Floorp thing happened, but I thought this is just a random incident not worth the extra attention.

It seems I was wrong. It is something that people should start paying attention to.

A lot of people – developers and users alike, intentionally or not – misuse the term Open Source, and some of them like FUTO even go that far to redefine it and create their own incompatible The Open Source Definition that will suit their own purpose.

Open Source Confusion Cases

Now, the main purpose of this post isn’t for me to let off some steam, but to share a great project started by Dan Brown of attempting to find and list all projects which claim to be Open Source while their licenses say otherwise.

It can be found on his Github account.

Share your views to FSFE

Albert pointed out that FSFE is also interested in this topic:

The FSFE is looking for examples and thoughts about openwashing if you feel like it I’m sure they’ll welcome your input mastodon.social/@johas/112524760073638652

Tuesday, 11 June 2024

We’re already on Week 3 of the coding period of Google Summer of Code! As a reminder, I’m adding Python support to a few KDE Frameworks. During the first two weeks I added support for KWidgetsAddons, and it’s now almost finished except for two widgets whose bindings aren’t generated properly and don’t compile.

I also wrote (with the help of my mentor Carl) the necessary CMake code to build the library. That part is probably going to end (hopefully) in extra-cmake-modules so it can be used by anyone easily. The plan is to eventually submit each of the bindings to their repository so it’s easier to keep the C++ libraries in sync with their Python bindings.

This week I will be adding some examples for KWidgetAddons now that it’s on a usable status. If you want to see how it’s going, you can take a look at the code.

Sunday, 9 June 2024

I’m a heavy user of Firefox profiles. Apart from using different profiles for different activities, I also have a few extra profiles that all run in the Default activity.

This means that I need to have different icons shown in Plasma’s panel in order to be able to easily differentiate which profile a window belongs to.

Sure, I use the tasks applet which shows the window title instead of the icon-only one (I prefer usability to minimalism), but still, it isn’t enough as sometimes the active tab in a Firefox window might not have the most informative title.

Plasma seems to rely on the application name and the window class when choosing the icon it will show in the panel. Which means that, by default, all Firefox instances end up having the same icon.

Librewolf with a custom profile icon
Librewolf with a custom profile icon

Fortunately, Firefox allows you to specify the window class it should use through command line arguments.

firefox -P ProfileName --class WindowClassName

And, to connect a launcher to a specific window class, you just need to add the following line to the .desktop file:

StartupWMClass=WindowClassName

So, in order to have a nicely supported Firefox profile, you can create a launcher with a desktop file similar to the following:

[Desktop entry]
Exec=firefox -P SocialSites --class FirefoxSocialSites
Icon=user-available-symbolic
StartupWMClass=FirefoxSocialSites

It also works with Firefox derivatives such as Librewolf (which can be seen in the screenshot above) and others.

Wayland

For Wayland users, a comment by John Kizer might be useful:

On Wayland, I’ve ended up just using KWin Window Rules (based on a substring of the window title, and setting the desktop file name) in combination with .desktop files that launch Firefox to the site in question and have the desired icon associated.

EDIT: And another approach for Wayland by Christoph Martin:

There’s no need for messing around with window rules - at least not for Firefox.

If you use the –main flag instead of the –class flag for the Firefox invocation in your .desktop file, you should get the desired effect - at least in the Icons-Only Task Manager. Note that StartupWMClass still needs to match the value of the –main flag.

The above works on my machine, that is under Plasma 6.0.5 on the Fedora 40 KDE spin.

Credit: https://superuser.com/a/1784867

Friday, 17 May 2024

强制类型转换

C++提供了四个强制类型转换的关键字:

  • static_cast
  • const_cast
  • reinterpret_cast
  • ``dynamic_cast`

static_cast

1
static_cast<目标类型>(表达式)
1
2
int num = 2;
double result =static_cast<double>(num);

该运算符将表达式转换为目标类型。但没有进行运行时类型检查来保证转换的安全性

主要用法

  1. 用于类层次结构中父类和子类之间指针或引用的转换.进行上行转换是安全的(即将子类的指针或引用转换成父类是正确的);进行下行转换的时候,由于没有动态类型检查,所以是不安全的。继承必须为public
  2. 用于基本类型之间的转换,如intchar安全性也需要程序员来保证
  3. 把空指针转换为目标类型的空指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Person
{
public:
void print()
{
cout << "Person " << endl;
}
};

class Son : public Person
{
public:
void print()
{
cout << "Son " << endl;
}
};

void print1(Person *p)
{
p->print();
}

int main()
{
Son s;
print1(static_cast<Person *>(&s));
return 0;
}

const_cast

const_cast是c++中专用于处理与const相关的强制类型转换的关键字

其功能为:为一个变量重新设定其const描述.

即:const_cast可以为一个变量强行增加或删除其const限定.

需要明确的是,即使用户通过const_cast强行去除了const属性,也不代表当前变量从不可变变为了可变。const_cast只是使得用户接管了编译器对于const限定的管理权,故用户必须遵守“不修改变量”的承诺。如果违反此承诺,编译器也不会因此而引发编译时错误,但可能引发运行时错误。

  1. const_cast可用于更改const成员函数内的非const类成员。
  2. const_cast可用于将const数据传递给不接收const的函数。
  3. const_cast<>里边的内容必须是引用或者指针。
  4. const_cast也可以用来抛弃volatile__unaligned属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Student
{
private:
int roll;
public:
Student(int r) :roll(r) {}
void fun() const
{
(const_cast<Student*> (this))->roll = 5;
}
int getRoll() { return roll; }
};

int main() {
Student student(3);
std::cout << "Old roll number: " << student.getRoll() << std::endl;
student.fun();
std::cout << "New roll number: " << student.getRoll() << std::endl;

// const_cast只能调节类型限定符,不能更改基础类型
int a1 = 40;
//const int* b1 = &a1;
//char* c1 = const_cast <char*> (b1); // 编译程序时出错

const volatile int* d1 = &a1;
std::cout << "typeid of d1 " << typeid(d1).name() << '\n'; // int const volatile *
int* e1 = const_cast <int*> (d1);
std::cout << "typeid of e1 " << typeid(e1).name() << '\n'; // int *

return 0;
}

在const成员函数fun()中,编译器将“this”视为“ const student const this”,即“this”是指向常量对象的常量指针,因此编译器不允许通过以下方式更改数据成员“这个”指针。const_cast将“this”指针的类型更改为“student const this”

const_cast比简单类型转换更安全。从某种意义上讲,如果强制类型与原始对象不相同,则强制转换不会发生,这是比较安全的。

1
2
3
int a=20;
const int *p=&a;
char *c1=const_cast<char*> (p);//编译时程序出错

reinterpret_cast

reinterpret,即重新解释.

该强制类型转换的作用是提供某个变量在底层数据上的重新解释.

当我们对一个变量使用reinterpret_cast后,编译器将无视任何不合理行为,强行将被转换变量的内存数据重解释为某个新的类型。用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。 它不检查指针类型和指针所指向的数据是否相同。

需要注意的是,reinterpret_cast要求转换的两个数据所占用的内存大小一致,否则会引发编译时错误.

1
data_type *var_name = reinterpret_cast <data_type *>(pointer_variable);

使用 reinterpret_cast 的目的:

  1. reinterpret_cast是一种非常特殊且危险的类型转换操作符。并且建议使用适当的数据类型使用它,即(指针数据类型应与原始数据类型相同)。
  2. 它可以将任何指针类型转换为任何其他数据类型。
  3. 当我们要使用位时使用它。
  4. 它仅用于将任何指针转换为原始类型。
  5. 布尔值将转换为整数值,即0表示false,1表示true。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class A {
public:
int a;
A(int i) :a(i) {}
void fun_a()
{
std::cout << "In class A\n";
}
};

class B {
public:
int b;
B(int i) :b(i) {}
void fun_b()
{
std::cout << "In class B\n";
}
};

void testReinterpretCast() {
B *x = new B(5);
A* y = reinterpret_cast<A*>(x);
y->fun_a(); // In class A
std::cout << y->a << std::endl; // 5
}

dynamic_cast

dynamic用于在运行时实现向下类型转换。

1
dynamic_cast <type-id> (expression)

expression转换为type-id类型,type-id必须是类的指针,类的引用或者是void,

如果type-id是一个指针,那么expression也是一个指针,是引用的话同为引用

特点如下:

  1. 它是在运行是进行处理的,其余三个都是在编译时完成. 运行时进行类型检查
  2. 不能用于内置的基本数据类型之间的强制转换
  3. dynamic_cast 要求 <> 内所描述的目标类型必须为指针或引用。dynamic_cast 转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回 nullptr
  4. 在类的转换时,在类层次上进行向上转换(子类指针指向父类指针),与static_cast的效果是一样的。在进行父类指针向子类指针的转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
  5. 向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。在C++中,编译期的类型转换有可能会在运行时出现错误,特别是涉及到类对象的指针或引用操作时,更容易产生错误。dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。
  6. 使用 dynamic_cast 进行转换的,基类中一定要有虚函数,否则编译不通过(类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义)。这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class AA {
public:
virtual void print() {
cout << "in class AA" << endl;
};
};

class BB :public AA {
public:
void print() {
cout << "in class BB" << endl;
};
};

void testDynamicCast() {
AA* a1 = new BB; // a1是A类型的指针指向一个B类型的对象
AA* a2 = new AA; // a2是A类型的指针指向一个A类型的对象

BB* b1, * b2, * b3, * b4;

b1 = dynamic_cast<BB*>(a1);// not null,向下转换成功,a1 之前指向的就是 B 类型的对象,所以可以转换成 B 类型的指针。
if (b1 == nullptr)
cout << "b1 is null" << endl;
else
cout << "b1 is not null" << endl;

b2 = dynamic_cast<BB*>(a2);// null,向下转换失败
if (b2 == nullptr)
cout << "b2 is null" << endl;
else
cout << "b2 is not null" << endl;

// 用 static_cast,Resharper C++ 会提示修改为 dynamic_cast
b3 = static_cast<BB*>(a1);// not null
if (b3 == nullptr)
cout << "b3 is null" << endl;
else
cout << "b3 is not null" << endl;

b4 = static_cast<BB*>(a2);// not null
if (b4 == nullptr)
cout << "b4 is null" << endl;
else
cout << "b4 is not null" << endl;

a1->print();// in class BB
a2->print();// in class AA

b1->print();// in class BB
//b2->print(); // null 引发异常
b3->print();// in class BB
b4->print();// in class AA
}

详细