expl3-strategy to automatically update the title of a document, depending on its content

Context

The current question arises:

Aim (sum up)

I have a main (beamer) document, say main.tex, the content of which varying, depending on the targeted audience. For this, main.tex inputs one or more of, say, 5 slave files: topic1.tex, topic2.tex, topic3.tex, topic4.tex, topic5.tex.

Each topic (file) has a topic title and I want the title of the main document to automatically be the list of the titles of the topics that are input (and only the ones that are input).

For this, I used the following strategy:

• At the beginning of each topic file, the topic title is specified, thanks to a \topictitle macro.
• Each time it is used, this macro appends the corresponding topic title to the right of a \g_topics_seq sequence.
• At the end of the (main) document:
• the content of \g_topics_seq sequence is placed in a \g_presentation_title_tl token list,
• the (not expanded: \exp_not:V) content of \g_presentation_title_tl is x-written to an auxiliary .sbj file.
• At the end of the preamble of the (main) document (hence at the next compilation):
• a scratch token list is x-set to contain the content of the auxiliary file,
• the content of this scratch token list is passed to \title.

The following MCE is the implementation of this strategy but I wonder if expl3 could provide a better one.

\documentclass{beamer}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{filecontents}
\usepackage[check-declarations]{expl3}
\usepackage{xparse}

\begin{filecontents*}{topic1}
\topictitle{% \TeX{},
\LaTeX{} and café%
}
\begin{frame}
\TeX{}, \LaTeX{} and café are nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic2}
\topictitle{Topic 2}
\begin{frame}
Topic 2 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic3}
\topictitle{Topic 3}
\begin{frame}
Topic 3 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic4}
\topictitle{Topic 4}
\begin{frame}
Topic 4 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic5}
\topictitle{Topic 5}
\begin{frame}
Topic 5 is nice!
\end{frame}
\end{filecontents*}

\ExplSyntaxOn

\seq_new:N \g_topics_seq
\tl_new:N \g_presentation_title_tl
\iow_new:N \g_output_stream

\NewDocumentCommand{\topictitle}{m}
{
\__topic_title:n {#1}
}

\cs_new_protected:Npn \__topic_title:n #1
{
\seq_gput_right:Nn \g_topics_seq {#1}
}

\AtEndDocument
{
\tl_gset:Nx \g_presentation_title_tl
{
\seq_use:Nn\g_topics_seq {,~}
}
\iow_open:Nn \g_output_stream { \c_sys_jobname_str.sbj }
\iow_now:Nx \g_output_stream { \exp_not:V\g_presentation_title_tl }
\iow_close:N \g_output_stream
}

\AtEndPreamble{%
\file_if_exist:nTF {\c_sys_jobname_str.sbj} {
\tl_set:Nx \l_tmpa_tl {\file_input:n {\c_sys_jobname_str.sbj}}
\exp_args:NV \title \l_tmpa_tl
}{
\title{No topic!}
}
}
\ExplSyntaxOff

\begin{document}
\maketitle{}
%
\input{topic1}
% \input{topic2}
\input{topic3}
% \input{topic4}
\input{topic5}
\end{document}

• it sounds a bit overdoing to use an extra file for this. Why don't you write to the aux-file? – Ulrike Fischer May 10 '19 at 14:57
• @UlrikeFischer I agree. That is what I tried first but, when I wanted to read the content of the .aux file, it was empty (I probably didn't read it at the right moment). IIRC, I also had some difficulties to write both a verbatim text (e.g.\title{ and }) and, inside, the (not expanded: \exp_not:V) content of \g_presentation_title_tl. – Denis Bitouzé May 10 '19 at 15:05
• You don't have to manually read the .aux file: it is automatically \input as part of the work done by \begin{document}, before the \AtBeginDocument hooks are run. – frougon May 10 '19 at 15:17
• @frougon Yes, I know that but when I tried to exploit what was in it, it simply didn't exist according to LaTeX's message. – Denis Bitouzé May 10 '19 at 15:38
• Proof needed. :-) – frougon May 10 '19 at 15:40

I would simply write to the aux-file:

\documentclass{beamer}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{filecontents}
\usepackage[check-declarations]{expl3}
\usepackage{xparse}

\begin{filecontents*}{topic1}
\topictitle{% \TeX{},
\LaTeX{} and café%
}
\begin{frame}
\TeX{}, \LaTeX{} and café are nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic2}
\topictitle{Topic 2}
\begin{frame}
Topic 2 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic3}
\topictitle{Topic 3}
\begin{frame}
Topic 3 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic4}
\topictitle{Topic 4}
\begin{frame}
Topic 4 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic5}
\topictitle{Topic 5}
\begin{frame}
Topic 5 is nice!
\end{frame}
\end{filecontents*}

\ExplSyntaxOn

\seq_new:N \g_topics_seq

{
\seq_gput_right:Nn\g_topics_seq {#1}
}

\makeatletter
\NewDocumentCommand{\topictitle}{m}
{
\iow_now:Nx \@mainaux
{
}
}

\title{\seq_use:Nn\g_topics_seq {,~}}

\ExplSyntaxOff

\begin{document}
\maketitle{}
%
\input{topic1}
% \input{topic2}
\input{topic3}
\input{topic4}
\input{topic5}
\end{document}


I'd do as Ulrike suggests, writing to the \@mainaux to avoid polluting the file streams. I'd also avoid expansion (mainly x-type) of the text, as this is likely to cause you trouble.

Note also that in your code the \tl_set:Nx \l_tmpa_tl {\file_input:n {\c_sys_jobname_str.sbj}} didn't do anything because \input is not expandable, so you ended up with \title{\input{\jobname.sbj}}.

The approach I propose is to, at the end of the document, write a \gdef\@input@topics{<stuff>} to the \@mainaux, which will be read in the next run. After the file is read then the \@input@topics is already known and can be simply used in the title. The written definition has to be global because the contents of the .aux file are read inside a group.

At the writing step I used e-type expansion in \seq_use:Nn because x-type expansion is too much, and because f-type would try to expand the first tokens of the title, which would expand \TeX unless there was a space before it. Everything is put in \exp_not:e to avoid the x expansion of \write. An extra \exp_not:N is needed before \@input@topics for later runs, where it is already defined.

Here's the code:

\documentclass{beamer}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{filecontents}
\usepackage[check-declarations]{expl3}
\usepackage{xparse}

\begin{filecontents*}{topic1}
\topictitle{\TeX{},
\LaTeX{} and café%
}
\begin{frame}
\TeX{}, \LaTeX{} and café are nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic2}
\topictitle{Topic 2}
\begin{frame}
Topic 2 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic3}
\topictitle{Topic 3}
\begin{frame}
Topic 3 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic4}
\topictitle{Topic 4}
\begin{frame}
Topic 4 is nice!
\end{frame}
\end{filecontents*}

\begin{filecontents*}{topic5}
\topictitle{Topic 5}
\begin{frame}
Topic 5 is nice!
\end{frame}
\end{filecontents*}

\ExplSyntaxOn

\seq_new:N \g_topics_seq

\NewDocumentCommand{\topictitle}{m}
{ \__topic_title:n {#1} }
\cs_new_protected:Npn \__topic_title:n #1
{ \seq_gput_right:Nn \g_topics_seq {#1} }

\makeatletter
\AtEndDocument
{
\iow_now:Nx \@auxout
{
\gdef \exp_not:N \@input@topics
{ \exp_not:e { \seq_use:Nn \g_topics_seq {,~} } }
}
}

\AtBeginDocument
{
\cs_if_exist:NTF \@input@topics
{ \exp_args:NV \title \@input@topics }
{ \title { No~topic! } }
}
\makeatother

\ExplSyntaxOff

\begin{document}
\maketitle{}
%
\input{topic1}
% \input{topic2}
\input{topic3}
% \input{topic4}
\input{topic5}
\end{document}

• Very nice explanations about expansions, that I still find hard to understand correctly. – Denis Bitouzé May 10 '19 at 15:35
• @DenisBitouzé Sorry if I couldn't explain it right. You mean the types of expansion (e, f, x)? If so you can take a look at interface3.pdf (texdoc interface3), section 3 Introducing the variants. – Phelype Oleinik May 10 '19 at 15:40