5

This is another question about protection and expansion, that I still don't understand well… (and I guess this is my problem here). I read especially How do I "unprotect" an argument?, and many others.

I'm trying to create automatic and contextual labels for titles (See below for details if needed). If I write eg \myref{My_title_here}, the link to the label is eg “A.II.3.d My title here”, and points to the 4th subsection of the 3rd section of the 2nd chapter in the 1st part. A.II.3.d is the “title number”, and is the result of \getrefnumber {#1} (here #1 is My_title_here), and I want to get it in \templabel, but:

    %\edef \templabel {\getrefnumber {#1}} doesn't work

What I want is to get the result (the expansion) of \getrefnumber {#1} (eg A.II.3.d) in \templabel. Again, the code is at the end if needed.


Workaround

\edef didn't work, so I finally tried \protected@edef (Are \protected\edef and \protected@edef the same?):

    \protected@edef \templabel {\getrefnumber {#1}}
    %\show \templabel->A.\protect \textlatin  {I}.1.

I finally found this to remove \protect:

    \expandafter \expandafter \expandafter \def \expandafter \expandafter \expandafter \templabel \expandafter \expandafter \expandafter {\getrefnumber {#1}}
    %\show \templabel ->A.\textlatin {I}.1.

I looked at the definition of \textlatin and noticed that it really looks like \textbf. It's a formatting command. To remove the \textlatin command, I use the following:

    \def \textlatin #1{#1}

Questions

Finally it works, but:

  1. Can you explain briefly what's the problem here, with protection and expansion? Why can't I use \getrefnumber {#1} directly?

  2. I would like to know is there is a better solution, or more general, or at least, if the solution covers all cases here: how to get directly A.I.1 instead of A.\protect \textlatin {I}.1?

  3. Does this solution with \expandafter works in each case? Which commands may I encounter here, in \getrefnumber {#1}: \textlatin, \textgreek (because I use \greek?) and what else?

  4. The way I deal with the \textlatin command is it the better one? Are there any drawbacks with this solution? (I don't understand the definition of \textlatin, which is like \textbf)

  5. And if I use - instead of . (A-II-3-4)? Does the - will be considered as an arithmetic operation, eg during expansion?

Feel free give advice on code, I'm learning.


The context, just in case

I use the following numbering:

\def \thepart          {\Alph   {part}}
\def \thechapter       {\Roman  {chapter}}
\def \thesection       {\arabic {section}}
\def \thesubsection    {\alph   {subsection}}
\def \thesubsubsection {\greek  {\value{subsubsection}}}
\setcounter {secnumdepth} {3} % Paragraphs and subparagraphs unnumbered

I create “informative” numbering for the titles (Source: (Re)define \ref for chapter that includes the part number):

\def \p@chapter       {\thepart.}
\def \p@section       {\thepart.\thechapter.}
\def \p@subsection    {\thepart.\thechapter.\thesection.}
\def \p@subsubsection {\thepart.\thechapter.\thesection.\thesubsection.}

Then I create my label command that I will patch to sectionning commands (Sources:
Sectioning command that automatically creates label,
Changing the output of \ref depending on the position of the corresponding \label,
How to change the appearance of \ref depending on where it's called relative to the \label,
Automatic creations of labels).

\newcommand {\myref }[1]{%
    \@ifundefined {r@#1} {??} {\begingroup
    %\edef \templabel {\getrefnumber {#1}} doesn't work

To get A.I.2.b in \templabel, this is the problem

Then I will split the string with xstrings:

    \StrCut \templabel         {.} \part@number         \part@tail
    \StrCut \part@tail         {.} \chapter@number      \chapter@tail
    % …
    \StrCut \paragraph@tail    {.} \subparagraph@number \subparagraph@tail

and create the contextual link (the link, his number is eg “A-III-2-b The title here” but if the link is in A-III, it will be only “2-b The title here”; it's contextual):

    \edef \templink {\getrefbykeydefault {#1} {anchor} {}}
    \IfStrEq \thepart \part@number
        {\IfStrEq \thechapter \chapter@number
            %…
            {\textcolor{red}{X} \hyperlink \templink \part@tail}}%
        {\textcolor{red}{X} \hyperlink \templink \templabel}%
    \endgroup}}

The MWE

\documentclass[greek]{book} % THE GREEK OPTION IS THE CAUSE

\usepackage {hyperref}
\usepackage {babel}

\makeatletter
\def \p@chapter       {\thepart.}
\def \p@section       {\thepart.\thechapter.}
\def \p@subsection    {\thepart.\thechapter.\thesection.}
\def \p@subsubsection {\thepart.\thechapter.\thesection.\thesubsection.}

\newcommand {\myref}[1]{%
    \@ifundefined {r@#1} {??} {\begingroup
        \edef \temp {\getrefnumber {#1}}
        % \show \temp->A.\textlatin {I}.1.
        \endgroup }}
\makeatother

\begin{document}
\mainmatter

\part{Hi :\\introduction}\label{Hi}
\chapter{Chapter}\label{Chapter}
See \myref{Test} below.
\section{Test}\label{Test}

\enddocument
user73438
  • 153
  • 6
  • This looks like pretty aggressive surgery. Are you sure existing packages won't do? E.g., nameref, cleveref, .... – jon Nov 24 '16 at 05:30
  • Can you provide a complete, minimal example? It should start with \documentclass and end with \end{document}. – Werner Nov 24 '16 at 05:39
  • Hi! Thanks for the fast reply! I don't know. I think \getrefnumber {#1} is a command from nameref, which I use. I read the documentation of cleveref, quickly, and others too, but didn't find anything. I prefer not to use too many packages, but if there is one… I'm still interested in the answer here. – user73438 Nov 24 '16 at 05:40
  • @Werner Ok, I start writing it now. I thought the answer could be an evidence. – user73438 Nov 24 '16 at 05:42
  • We've got a number of questions which cover fragile and robust commands. Bottom line is that things which are robust/protected cannot safety be expanded as they do things that TeX doesn't allow by expansion. – Joseph Wright Nov 24 '16 at 06:50
  • 1
    The answer to the general question in the title, is "you can't, that's why the command was protected as it will fail in an expansion context" In some special circumstances you can define alternative commands that do work via expansion, but it depends on the exact details of what you want to do. – David Carlisle Nov 24 '16 at 07:44
  • @DavidCarlisle — Thanks. If I understand well, depending on the protection mechanism (because I just learned there are 2), unprotecting is impossible, even if a special context would permit a safe expansion. The command is protected forever. The only way is too redefine locally ({\def …}) the command to do the same job as the protected command. And that's what I've done. There is no other way. Am I right? – user73438 Nov 24 '16 at 09:14
  • @user73438 yes but in general it is not possible to define a command that does the job via expansion. consider \DeclareRobustCommand\foo{\def\zzz{aaa}[\zzz]} if you really just need [aaa] then you can define \def\foo{[aaa]} which is the equivalent of what you have done with textlatin, but if you actually need \zzz to be defined after the call to \foo then there is no way for it to work. Similarly your textlatin redefinition only works as you don't actually need to switch font encodings in this context. – David Carlisle Nov 24 '16 at 09:20
  • http://tex.stackexchange.com/questions/66118/advantages-and-disadvantages-of-fully-expandable-macros/66168#66168 – David Carlisle Nov 24 '16 at 09:28
  • @DavidCarlisle — Ok, I read it. So there are commands which expands without error in expansion-only context (en \edef), and commands that rely on commands unexpandable by nature (assignments like \def, text typesetting like \textbf or \textlatin, …), so they will throw errors if expanded in expansion-only context, so they are protected. In some cases, eg if the text is not typeset and I want only access to the plain text itself, like in this question, I cannot remove the protection but I can simulate the behavior of the original command by redefining it locally in a suitable way. – user73438 Nov 24 '16 at 10:36
  • @DavidCarlisle — Concerning the 2 protection mechanisms, one is safest than the other and rely on ε-TeX: \protected (\protected\edef, \protected\def…). The other one, based on LaTeX2e, uses \protect, DeclareRobustCommand, \protected@\edef… Are there other command names associated with these mechanisms? — I'm still not sure if one is (always) better than the other, or if there are use cases when I should use one or the other — As a guideline to code, it's better to write expandable commands (when it's possible), or to protect them (to prevent errors, but rewrite them when needed). – user73438 Nov 24 '16 at 10:57
  • @DavidCarlisle — Please tell me if I'm wrong. – user73438 Nov 24 '16 at 11:00
  • too long to answer that in a comment see the linked questions or search for protect there are several longish examples giving examples. \newcommand also defines \protect-ed commands if used with an optional argument as do several other latex constructs. (almost) all expl3/xparse declarations use the etex \protected mechanism – David Carlisle Nov 24 '16 at 11:02

1 Answers1

1

Partial answer, no time for more no.

\getrefnumber comes from package refcount and is full expandable. That means, it can be used inside \edef. But if the reference contains breakable stuff, then \getrefnumber will safely extract this reference, but the reference itself will break. In your case, \textlatin is introduced (probably by babel), which breaks by the hard expansion of \edef.

Since \textlatin is a macro that follows LaTeX's protection protocol, the expansion via \protected@edef is safe as you have observed.

Macros can be protected in LaTeX by \DeclareRobustCommand. Then e-TeX came up, that offers \protected to the definition commands to make a macro robust and prevents expansion in expandable contexts as inside \edef, writing to file.

\protected\edef\macro{definition text} makes a protected \macro and expands the definition text the hard way. \protected@edef makes a normal (unprotected) \macro and expands the definition text with LaTeX's protection mechanism in force.

BTW, if you use the expandable extraction commands of package refcount like \getrefnumber, then \refused should be used with the same label outside the expansion context to get proper warnings for undefined references.

Heiko Oberdiek
  • 271,626
  • Thanks! The comment of @DavidCarlisle complete this answer. I will search information on the differences between the 2 protection mechanisms. What I don't understand is the role of \refused. Could you elaborate? – user73438 Nov 24 '16 at 09:20
  • Throwing a warning is a non-expandable operation in LaTeX. If \getrefnumber is used inside \edef (or \protected@edef), then the warning will become unexpanded garbage and nothing is printed in the screen or .log file. \refused is called outside the expandable context (e.g., before or after the definition with \etex), notifies the user with a warning, if the reference is still undefined, and tells LaTeX that there is an undefined reference. Then LaTeX can print its "rerun" warning at the end of the LaTeX job. – Heiko Oberdiek Nov 24 '16 at 17:35
  • Thanks you! For the record, there is in the first answer, an example of usage : http://tex.stackexchange.com/questions/252994/changing-the-output-of-ref-depending-on-the-position-of-the-corresponding-labe – user73438 Nov 24 '16 at 20:47