I am currently trying my hand at latex3 to get a deeper understanding of programming with latex. For this I have created a simple settings system.
It works as follows:
- You create a setting via
\NewSetting[type = <type>, init = <init-value>]{<Name>} - Based on its type it calls its corresponding setting macro: e.g. for a string setting it will call
\NewStringSetting[<init-value>]{<Name>}. \NewStringSettingwill create the string, set the initial value of the string and create all needed macros to use the string, e.g.Clear<Name>,Set<Name>andGet<Name>.
Example | A string setting for author:
- I create a setting by calling
\NewSetting[type = string, init = Dave]{Author} - Internal:
\NewSettingcalls\NewStringSetting[Dave]{Author} - Internal:
\NewStringSettingcreates an expl3 string (g_my_author_str), sets if needed a value and creates 3 macros:\ClearAuthor,\SetAuthor{<Content>}and\GetAuthor.
Now I can clear, set and get the content for the string author.
Problem: I use xkeyval for key-value pairs. They get set in \NewSetting via \setkeys within a group so they reset after leaving the group (see David Carlisle's comment). But then all created macros (\ClearAuthor, \SetAuthor{<Content>} and \GetAuthor) are undefined after leaving the group. A solution would be to get rid of the group but then the key-value pairs wont reset as intended. So not really a solution. Then I stumbled across @wipet's solution to forward the macros out of the group; thus keeping the macros while still all keys reset at the end of the group. Looks like a working solution.
Question: I am still quite new to LaTeX, especially latex3. I'm fairly satisfied with the result so far (it works, after all) but I'd like to know what you think. I would like to know if there is an easier way and what I could improve.
The biggest difficulty I had was expansion. Are there any good references (e.g. Youtube, forum posts, etc.)? (especially those that also address latex3 and their primitives like \exp_last_unbraced ...)
Summa summarum:
- how could the code be improved (especially with regard to the problem I described)?
- are there any good references explaining latex3 (I know of LaTeX3's manual)?
- is there some rule of thumb to get expansion right without trial and error
Code of string setting:
\documentclass{book}
\usepackage{expl3} % Only needed for IDE-autocomplete (aka IntelliSense)
\usepackage{xkeyval}
\makeatletter
\ExplSyntaxOn
% By egreg https://tex.stackexchange.com/a/63233/293060
\NewExpandableDocumentCommand{\IfNoValueOrEmptyTF}{m m m}{%
\IfNoValueTF{#1}{#2}{%
\tl_if_empty:nTF{#1}{#2}{#3}%
}%
}%
% By wipet https://tex.stackexchange.com/a/690710/293060
\def\keepaftergroup#1{%
\global \expandafter\let \csname x:\string#1\endcsname =#1
\aftergroup\let
\aftergroup#1%
\expandafter\aftergroup \csname x:\string#1\endcsname
}
\define@key[DAVE]{settings}{type}{\def\DAVE@Settings@Type{#1}}
\define@key[DAVE]{settings}{init}{\def\DAVE@Settings@Init{#1}}
\NewDocumentCommand{\NewSetting}{o m}{%
\begingroup
\setkeys[DAVE]{settings}{#1}%
\cs_if_exist:NTF\DAVE@Settings@Type{%
\str_case:NnF{\DAVE@Settings@Type}{%
{string}{\begingroup\edef\x{\endgroup\noexpand\NewStringSetting[\cs_if_exist:NT\DAVE@Settings@Init{\DAVE@Settings@Init}]{#2}}\keepaftergroup\x} % <--- Wont work without \keepaftergroup, only if \begingroup + \endgroup removed (but then keys wont reset)
%{bool}{...}
}{%
\ClassError{SETTINGS}{Unknown~settings~type~for~setting~'#2'}{}
}
}{%
\ClassError{SETTINGS}{Cannot~create~setting~'#2'~due~to~missing~setting~type}{}
}
\endgroup
\x % <--- Calling
}
\NewDocumentCommand{\NewStringSetting}{o m}{%
\str_if_exist:cTF{g_DAVE_#2_str}{%
\ClassError{SETTINGS}{String~with~the~name~'#2'~already~exist}{}
}{%
\str_gclear_new:c{g_DAVE_#2_str}
\IfValueT{#1}{%
\str_gset:cn{g_DAVE_#2_str}{#1}
}
\expandafter\NewExpandableDocumentCommand\expandafter{\csname Clear#2\endcsname}{}{%
\str_gclear:c{g_DAVE_#1_str}
}
\expandafter\NewExpandableDocumentCommand\expandafter{\csname Set#2\endcsname}{m}{%
\IfNoValueOrEmptyTF{##1}{%
\ClassWarning{SETTINGS}{Could~not~set~value~for~setting~'#2'~due~to~the~passed~value~being~of~type~'NoValue'~or~empty}%
}{%
\str_gset:cn{g_DAVE_#2_str}{##1}%
}%
}
\expandafter\NewExpandableDocumentCommand\expandafter{\csname Get#2\endcsname}{}{%
\str_use:c{g_DAVE_#2_str}
}
}
}
\ExplSyntaxOff
\makeatother
\NewSetting[type = string]{Forum}
\NewSetting[type = string, init = Dave]{Author}
\SetForum{StackExchange}
\begin{document}
Hello \TeX-\GetForum \space it's \GetAuthor
\end{document}