1

My main focus is to extend the already given sample for more sectionings by additional features (e.g. subtitle). While I succeed with the subtitle I struggled with the macro query about the \expandafter. Thanks to the provided samples I figured out how a working sample could look like.

\documentclass{article}
\usepackage{keyval}


\makeatletter
\define@key{mosquito}{hideInToC}[false]{\def\mosquito@hideInToC{#1}}
\newcommand\sectionLvl[3][]{%
    \setkeys{mosquito}{hideInToC={},#1}%
    \ifcase#2\relax\expandafter\chapter\or
    %section
    \ifx\mosquito@hideInToC\@empty\expandafter\expandafter\expandafter\section%
    \else\expandafter\expandafter\expandafter\section\expandafter\expandafter\expandafter*%
    \fi
    \else\expandafter\subsection%
    \fi
    {#3}
}%
\makeatother

\begin{document}
    \tableofcontents

++++++++++ after ToC********

    \sectionLvl{1}{title}
    \sectionLvl[hideInToC]{1}{title1}
    \sectionLvl{1}{title2}
    \sectionLvl{2}{title2-1}
\end{document}

Which works in general but leaves some questions open from the previous answers. My two questions are:

  1. According the keyval docu I can define a boolean value. But the docu lacks of a sample how to check the value. \ifx\mosquito@hideInToC\@empty is working - but is it the best way to check a boolean value?
  2. In the given answers for the macro query I found solutions like \ifnum#2=1 \expandafter\@firstoftwo\fi\section*{#1}% which look great. But when I try to apply it here with \ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\fi\section*% the evalutation fails always.

So in total I have a working solution. But I'm curious about the details why it fails and why the proposed approaches fail in the given scenario.

2 Answers 2

2

Ad question 1:

I think the classic way of handling the boolean would be using boolean keys as provided by packages like xkeyval or pgfkeys or by expl3.

If you don't like to use such packages/interfaces, you can define your own mechanism for cranking out by means of delimited arguments whether the value "true/yes" or the value "false/no" or something else is provided and setting an \if⟨name of if⁠-⁠switch⟩-switch accordingly by applying one of the commands \⁠⟨name of if⁠-⁠switch⟩true/\⁠⟨name of if⁠-⁠switch⟩false:

\documentclass{article}
\usepackage{keyval}

\makeatletter
%
%----------------------------------------------------------------------------------------------
%
% \truefalsefork{<value>}%
%               {<tokens if <value>=true/yes>}%
%               {<tokens if <value>=false/no>}%
%               {<tokens> if <value> neither is true/yes nor is false/no}
%
\@ifdefinable\gobbletorelax{\long\def\gobbletorelax#1\relax{}}%
\newcommand\truefalsefork[1]{%
  \lowercase{\@truefalsefork{#1}}%
}%
\newcommand\@truefalsefork[4]{%
  \ifcat$\detokenize\expandafter{\gobbletorelax#1\relax}$%
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {%
    \selecttruefalsecase
    \relax#1\relax yes\relax false\relax no\relax{#2}%
    \relax true\relax#1\relax false\relax no\relax{#2}%
    \relax true\relax yes\relax #1\relax no\relax{#3}%
    \relax true\relax yes\relax false\relax#1\relax{#3}%
    \relax true\relax yes\relax false\relax no\relax{#4}\relax\relax\relax\relax
  }{#4}%
}%
\@ifdefinable\selecttruefalsecase{%
  \long\def\selecttruefalsecase#1\relax true\relax yes\relax false\relax no\relax#2#3\relax\relax\relax\relax{#2}%
}%
%
%----------------------------------------------------------------------------------------------
%
% Infrastructure for boolean-key hideInToc
%
\newcommand\hideInTocErrordefault[1]{%
   \PackageError{sectionLvl}{%
      Macro \string\sectionLvl:\MessageBreak 
      Invalid value '\detokenize{#1}'\MessageBreak
      for key hideInToC\on@line.\MessageBreak
      Value "true" is assumed%
   }{%
      Only values true/false/yes/no are allowed!\MessageBreak 
      (Lettercasing doesn't matter.)\MessageBreak
      Value "true" is assumed.%
   }%
   \hideInToctrue
}%
\newif\ifhideInToc
\define@key{mosquito}{hideInToC}[true]{%
  \truefalsefork{#1}{\hideInToctrue}{\hideInTocfalse}{\hideInTocErrordefault{#1}}%
}%
%
%----------------------------------------------------------------------------------------------
%
\newcommand\sectionLvl[3][]{%
    \setkeys{mosquito}{hideInToC=false, #1}%
    \ifcase#2\relax
      \expandafter\chapter
    \or
      %section
      \ifhideInToc\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
      {\expandafter\section\expandafter*}{\expandafter\section}%
    \else
      \expandafter\subsection
    \fi
    {#3}%
}%
\makeatother


\begin{document}
    \tableofcontents

%++++++++++ after ToC********

    \sectionLvl{1}{title}
    \sectionLvl[hideInToC=TrUe]{1}{title1}
    % Check the error-message-mechanism by uncommenting the next line:
    % \sectionLvl[hideInToC=WeiRd]{1}{title1}
    \sectionLvl{1}{title2}
    \sectionLvl{2}{title2-1}

\end{document}

Be aware that this is sort of a poor-man's implementation of a boolean-key.

The packages mentioned above provide more sophisticated routines.

Ad question 2:

When looking at the code presented by you, I see the following:

As the \ifx\mosquito@hideInToC\@empty..\else..\fi-thingie is nested within the last \or-branch of an \ifcase..\or..\else..\fi-thingie, both within the "true"-branch and within the "false"-branch of the \ifx\mosquito@hideInToC\@empty..\else..\fi-thingie you have taken measures for ensuring that an \expandafter-chain is present which triggers expansion of the subsequent \else of the surrounding \ifcase..\or..\else..\fi-thingie which in turn leads to the removal of the entire "surrounding" "else"-branch and the \fi before \section/\section* gets carried out. Thus due to these \expandafter-chains both \section from the \ifx\mosquito@hideInToC\@empty..\else..\fi-thingie's "false"-branch and \section* from the \ifx\mosquito@hideInToC\@empty..\else..\fi-thingie's "true"-branch can fetch {#3} as its argument as no other tokens stemming from the surrounding \ifcase..-expression are in the way.

If you just replace the \ifx\mosquito@hideInToC\@empty..\else..\fi-thingie by something like the sequence \ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\fi\section*, then such an \expandafter-chain is not there. Thus the tokens \else..\fi from the surrounding \ifcase..\or..\else..\fi-thingie are still there when the argument of \section/\section* is gathered. Thus not the sequence {#3} but the token \else will be the argument of \section/\section*, leading to unexpected results/unexpected behavior.

You could define an additional macro \@twooftwo and do:

\documentclass{article}
\usepackage{keyval}


\makeatletter
\define@key{mosquito}{hideInToC}[false]{\def\mosquito@hideInToC{#1}}
\newcommand\@twooftwo[2]{#1#2}
\newcommand\sectionLvl[3][]{%
    \setkeys{mosquito}{hideInToC={},#1}%
    \ifcase#2\relax
      \expandafter\chapter
    \or
      %section
      \ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\else\expandafter\@twooftwo\fi
      {\expandafter\section}{\expandafter*}%
    \else
      \expandafter\subsection
    \fi
    {#3}%
}%
\makeatother

\begin{document}
    \tableofcontents

++++++++++ after ToC********

    \sectionLvl{1}{title}
    \sectionLvl[hideInToC]{1}{title1}
    \sectionLvl{1}{title2}
    \sectionLvl{2}{title2-1}
\end{document}

You could also do the "standard" \if..\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi-thingie:

\documentclass{article}
\usepackage{keyval}


\makeatletter
\define@key{mosquito}{hideInToC}[false]{\def\mosquito@hideInToC{#1}}
\newcommand\sectionLvl[3][]{%
    \setkeys{mosquito}{hideInToC={},#1}%
    \ifcase#2\relax
      \expandafter\chapter
    \or
      %section
      \ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
      {\expandafter\section}{\expandafter\section\expandafter*}%
    \else
      \expandafter\subsection
    \fi
    {#3}%
}%
\makeatother

\begin{document}
    \tableofcontents

++++++++++ after ToC********

    \sectionLvl{1}{title}
    \sectionLvl[hideInToC]{1}{title1}
    \sectionLvl{1}{title2}
    \sectionLvl{2}{title2-1}
\end{document}

But these are just examples exhibiting how to get the \expandafter-tokens in line for removing the \ifcase..\or..\else..\fi-thingie's \else..\fi before carrying out \section/\section* and hereby having TeX gather \section/\section*'s argument.

I don't recommend this because you don't have infrastructure for handling the situation of the user providing an inappropriate value for the key hideInToC.

2
  • Thx a lot for your elaborated response. I highly appreciate to get an idea what's going on behind the curtain. I get a better understanding about checks and how conditional branches are expanded. As well as how TeX handles tokens. Just a quick remark to your sample: The very last sample is compiling while your alternative isn't. But this doesn't harm much since I guess its just for sketching a possible alternative.
    – LeO
    Commented Jan 18, 2021 at 7:40
  • @LeO The alternative is compiling now, too: I made the mistake ;-( of defining \newcommand\twooftwo... and using \@twooftwo. (The @ was missing in one place.) Thus \@twooftwo was undefined. Now it is \newcommand\@twooftwo.... ;-) I appreciate questions which provide opportunity to explain how things get processed by TeX and thus to provide pieces of a "mosaic", the totality of which, over time, provides an overview of how the different stages of processing .tex-input work and intertwine. Commented Jan 18, 2021 at 11:01
1

A simple boolean option can be defined by package kvoptions:

\documentclass{article}
\usepackage{keyval}
\usepackage{kvoptions}

\SetupKeyvalOptions{
  family=mosquito,
  prefix=mosquito@,
}
\DeclareBoolOption{hideInToC}

\makeatletter
\newcommand\sectionLvl[3][]{%
  \begingroup
    \setkeys{mosquito}{#1}%
    \ifcase#2\relax  % 0 = chapter
      \def\tmp@mosquito{\chapter}%
    \or  % 1 = section
      \ifmosquito@hideInToC
        \def\tmp@mosquito{\section*}%
      \else
        \def\tmp@mosquito{\section}%
      \fi
    \else  % subsection
      \def\tmp@mosquito{\subsection}%
    \fi
  \expandafter\endgroup
  \tmp@mosquito{#3}
}%
\makeatother

\begin{document}
    \tableofcontents

    \bigskip
    \hrule

    \sectionLvl{1}{Title 1 (shown)}
    \sectionLvl[hideInToC]{1}{Title 2 (hideInToC)}
    \sectionLvl{1}{Title 3 (shown)}
    \sectionLvl{2}{SubTitle 4 (shown)}
\end{document}

enter image description here

Remarks:

  • The \expandafter chains are replaced by a simple temporary macro \tmp@mosquito definition to ease readability and maintenance of the code. We do not need expandability here, because neither \chapter nor \section are fully expandable.
  • It is not fully clear, whether key hideInToC should act globally or locally. The usage implies local scope. Therefore, I have put the key setting inside a group, thus that hideInToC applies to the current \sectionLvl only.

Ad 1:

With package keyval and without package kvoptions, a boolean option definition consists of three parts:

  1. Definition of a boolean switch:

     \newif\ifmosquito@hideInToC
    
  2. Optional. The default is false as usually expected. It can be changed to true:

     \mosquito@hideInToCtrue
    
  3. Option definition:

     \define@key{mosquito}{hideInToC}[true]{%
       \csname mosquito@hideInToC#1\endcsname
     }
    

    The only allowed values are true and false. Other values are ignored, because \csname...\endcsname behaves as \relax if the command sequence is not defined.

    It is possible to add an error check:

     \define@key{mosquito}{hideInToC}[true]{%
       \ifcsname mosquito@hideInToC#1\endcsname
         \csname mosquito@hideInToC#1\endcsname
       \else
         % Error message (\PackageError, \latex@error, ...)
       \fi
     }
    

Usage forms:

    hideInToC       % true
    hideInToC=true  % true
    hideInToC=false % false

It is possible to define an opposite boolean option:

    \define@key{mosquito}{showInToC}[true]{%
      \expandafter\ifx\csname if#1\endcsname\iftrue
        \mosquito@hideInToCfalse
      \else
        % Caution: \iffalse inside \if constructs needs to be protected
        \expandafter\ifx\csname if#1\expandafter\endcsname\csname iffalse\endcsname
          \mosquito@hideInToCtrue
        \else
          % Error message
        \fi
      \fi
    }

Then: showInToC % hideInToC=false showInToC=true % hideInToC=false showInToC=false % hideInToC=true

It is a one-liner with package kvoptions:

\DeclareComplementaryOption{showInToC}{hideInToC}

        
9
  • I'm highly appreciate your explanation in detail. Thx very much. I think go for the kvoptions although this seems another mystery. As far as I understood I can declare a boolean and than easily query it by \def\tmp@mosquito. If I want to have a subtile I need to use \DeclareStringOption{subtitle} and I guess with \setkeys the value is set as well. But how to access it from within \sectionLvl?
    – LeO
    Commented Jan 18, 2021 at 13:06
  • Another comment: If I stick to the keyval option than I would use \ifmosquito@hideInToC but this somehow fails. Perhaps I use it in the wrong way. Anyway - I won't use both options. Thx for clarifying different options.
    – LeO
    Commented Jan 18, 2021 at 13:08
  • @LeO The boolean option is unrelated to \def\tmp@mosquito. The boolean option is processed and the result (\chapter, \section, ...) is stored in the temporary macro \tmp@mosquito, to avoid lots of \expandafter. Commented Jan 18, 2021 at 13:25
  • @LeO \DeclareStringOption{subtitle} with the \SetupKeyvalOptions as in the example saves the option setting in macro \mosquito@subtitle. Commented Jan 18, 2021 at 13:26
  • @LeO Maybe you have forgotten \newif\ifmosquito@hideInToC if you define the boolean option without package kvoptions. Commented Jan 18, 2021 at 13:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .