class: title-slide # Replace Replace ## There's more than one way to replace a string Adriaan de Groot January 19th, 2020 --- ## This is a talk about strings --- ## This is a presentation about strings --- ## This is a lightning talk about string-substitution --- ## This is a ${talk_type} about ${subject} --- # String Constants - Very important, but boring. ``` static const char hello[]="Hello World"; static const QString hi ("Hello World"); static const QString hoi=QStringLiteral("Hello World"); ``` - Support translation through lookup. ``` QString greet=QObject::tr("Hello World"); QString salut=i18n("Hello World"); ``` --- # String Unconstants ## Shooting yourself in the foot - C style ``` char buffer[200]; strcpy(buffer, "Hello"); strcat(buffer, "World"); ``` - Qt style ``` QString buffer("Hello"); buffer += QString("World"); ``` --- # String Arguments - C style ``` char buffer[200]; sprintf(buffer, "Hello %s", "World"); ``` - Qt style ``` QString buffer = QString("Hello %1").arg("World"); ``` *Multiple arguments* use more format string elements. *Translation* applies to the format string. --- # String Arguments ## Drawbacks of numbered arguments - Re-use is harder - Order is important - Mistakes are harder to track down ``` QString buffer = QString("Griffel %2 op een %4 zonder %3") .arg("tweemaal", "goud", "papier", "morsen"); ``` --- # Features Needed - meaningful notation - support re-ordering - support translation - support terseness For *human* texts but also for *machine* texts. ``` Exec=kcharselect --qwindowtitle %c ``` --- # Enter KMacroExpander - Only *one* step above numbered arguments - There are much more extensive and fancy engines --- # Enter KMacroExpander - Part of KCoreAddons - Part of KDE Frameworks - Lightweight libraries on top of Qt - Liberally licensed --- # Finding KMacroExpander - Using CMake is easiest ``` find_package(ECM 5.65 NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH}) find_package(KF5 QUIET COMPONENTS CoreAddons) ``` - Then link to that component ``` target_link_libraries(myexecutable PUBLIC KF5::CoreAddons) ``` --- # Including KMacroExpander - `#include
` - Conveniences live in `KMacroExpander` namespace - Base classes are `KWordMacroExpander` and `KCharMacroExpander` --- class: title-slide # But what does it *do*? --- # Convenient KMacroExpander Convenience methods in namespace `KMacroExpander` - Perform safe macro expansion (substitution) on a string. - The escape char must be quoted with itself to obtain its literal representation in the resulting string. There are three dimensions for variation: - Using single letters or whole words for macro names - Substituting with strings or lists of strings - Apply shell quoting (or not) --- # Convenient KMacroExpander .left-column[ - `%% Title: %u:%n` - `--caption %n %u` - `Title: %{url}-%name` ] .right-column[ - `% Title: /tmp/myfile.txt:My File` - `--caption 'My File' '/tmp/myfile.txt'` - `Title: /tmp/myfile.txt-My File` ] --- # Convenient KMacroExpander Macro names can consist of chars in the range `[A-Za-z0-9_]`; use braces to delimit macros from following words starting with these chars, or to use other chars for macro names. .left-column[ - `%{🐘}` - `%e` - `%word!` ] .right-column[ - `Elephant` - `Excellent` - `Up!` ] --- # Convenient KMacroExpander ``` QString format("Hello, %{subject}. Here is an %{animal}."); ``` Meaningful, re-orderable, translatable *format*! --- # Convenient KMacroExpander ``` QString format("Hello, %{subject}. Here is an %{animal}."); QHash
replacement; replacement.insert("subject", "World"); replacement.insert("animal", "elephant"); ``` Translatable, computable *replacements*. --- # Convenient KMacroExpander ``` QString format("Hello, %{subject}. Here is an %{animal}."); QHash
replacement; replacement.insert("subject", "World"); replacement.insert("animal", "elephant"); QString result = KMacroExpander::expandMacros(format, replacement); ``` --- # Convenient KMacroExpander - `expandMacros()` in 4 variants - `QChar` vs `QString` keys - `QString` vs `QStringList` values - `expandMacrosShellQuote()` in 4 variants --- # Fancy KMacroExpander Implement a subclass of - `KWordMacroExpander` - `KCharMacroExpander` Implement the pure virtual `expandMacro()` in that subclass. --- # Fancy KMacroExpander The `expandMacro()` function does all the work: - it gets a *macro name* - it **may** write a *macro substitution* - returns `true` if it did --- # Fancy KMacroExpander - use a `QMap` instead - or a `QVariantMap` - implement tiered names - read from `Q_PROPERTY` - query a database --- # Fancy KMacroExpander ``` class MapExpander : public KWordMacroExpander { public: using Map = QMap
; MapExpander(const Map& map) : m_map(map) {} bool expandMacro(const QString& word, QStringList& replace) override { if (m_map.contains(word)) replace << m_map[word]; return m_map.contains(word); } private: const Map& m_map; }; ``` --- # Get Involved - Working on Frameworks is special - There are **probably** no real bugs - .. but new features might be cool - .. there's a documentation error on line 239 --- # Get Involved - [API Docs](https://api.kde.org/frameworks/kcoreaddons/html/namespaceKMacroExpander.html) - [Source](https://cgit.kde.org/kcoreaddons.git/) - [Get Involved](https://community.kde.org/Get_Involved) (general) - [Frameworks](https://community.kde.org/Frameworks)