0

How can I have a variable in variable name while writing to aux file in the answer on this link: https://tex.stackexchange.com/a/416272/192717

For example, from the above link:

\edef\mysuffix{helloworld}
% define variable with variable suffix \mysuffix
\definevalue{tester\mysuffix}{42}

% and use it elsewhere
\usevalue{tester\mysuffix}

Currently, I get this understandable error:

! Undefined control sequence.
<argument> usevalue@tester\mysuffix

l.37 ... \global \@namedef {tester\mysuffix }...

AFAIK, the problem will be resolved if I we are able to expand and write the variable name in the aux file, something like this:

\global \@namedef {testerhelloworld }...
codepoet
  • 1,316
  • 3
    please edit the question to make a single complete example that reproduces the error. macros expand by default as being written to a file so it is hard to understand your question, and you do not show the error that you got. – David Carlisle Dec 12 '19 at 12:49
  • 1
    By "complete", David means starting with \documentclass and ending with \end{document}. – Steven B. Segletes Dec 12 '19 at 13:47

2 Answers2

1

Don't use \unexpanded.

\documentclass{article}

\makeatletter
\newcommand{\usevalue}[1]{%
  \ifcsname usevalue@#1\endcsname
    \csname usevalue@#1\endcsname
  \else
    ??%
  \fi
}
\newcommand{\definevalue}[2]{%
  \begingroup\edef\x{\endgroup
    \write\@auxout{%
      \global\string\@namedef{usevalue@#1}{#2}%
    }%
  }\x
}
\makeatother

\def\mysuffix{helloworld}

\begin{document}

\usevalue{tester\mysuffix}

\definevalue{tester\mysuffix}{42}

\end{document}

This prints “42”.

The contents of the .aux file is

\relax 
\global \@namedef{usevalue@testerhelloworld}{42}

However, the original code works, provided you define \mysuffix before \begin{document} as it should.

\documentclass{article}

\makeatletter
\newcommand{\usevalue}[1]{%
  \ifcsname usevalue@#1\endcsname
    \csname usevalue@#1\endcsname
  \else
    ??%
  \fi
}
\newcommand{\definevalue}[2]{%
  \write\@auxout{%
    \unexpanded{\global\@namedef{usevalue@#1}{#2}}%
  }
}
\makeatother

\newcommand{\mysuffix}{helloworld}

\begin{document}

Something with \usevalue{tester\mysuffix}.

Something else.

Now we can define \texttt{tester} and use again it: \usevalue{tester\mysuffix}.

\definevalue{tester\mysuffix}{42}

\end{document}
egreg
  • 1,121,712
0

You can implement infrastructure for defining and retrieving values analogous to the existing \label-\ref-mechanism of the LaTeX 2ε-kernel.

The example below provides the following user-macros:

  • \valueundefinederror{&langle;name of value&rangle;}

    Triggers an error-message if value is undefined, else do nothing.
    Corresponds to \refused of Heiko Oberdiek's refcount-package.

  • \usevalue[{&langle;tokens in case value is undefined&rangle;}]{&langle;name of value&rangle;}

    If no optional argument is provided, \valueundefinederror{&langle;name of value&rangle;}\nfss@text{\reset@font\bfseries??} is delivered in case value is undefined.
    Corresponds to \ref of the LaTeX 2ε-kernel if no optional argument is provided.

  • \usevalueexpandable{&langle;name of value&rangle;}{&langle;tokens in case value is undefined&rangle;}

    Corresponds to \getrefnumber of Heiko Oberdiek's refcount package.

  • \immediatedefinevalue{&langle;name of value&rangle;}{&langle;tokens to deliver when using the value&rangle;}

    Almost corresponds to \def\@currentlabel{...}...\label{...} of the LaTeX 2ε-kernel.
    Unlike with \label, the \write to .aux-file is done immediately.)

  • \definevalue{&langle;name of value&rangle;}{&langle;tokens to deliver when using the value&rangle;}

    Corresponds to \def\@currentlabel{...}...\label{...} of the LaTeX 2ε-kernel.
    Like with \label, the \write to .aux-file is done delayed.

    Both with \immediatedefinevalue and with \definevalue expansion of the &langle;tokens to deliver when using the value&rangle; and expansion of the tokens that form the &langle;name of value&rangle; is not prevented at the time of writing data to .aux-file.

The example below provides some hooking into the infrastructure of the LaTeX 2ε-kernel for delivering warning-messages/error-messages in case of values being changed/undefined/multiply defined.

\documentclass[landscape]{article}

\makeatletter
%%=====================================================================================
%% Layout of example document
%%=====================================================================================
\@ifundefined{pagewidth}{}{\pagewidth=\paperwidth}
\@ifundefined{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}
\@ifundefined{pageheight}{}{\pageheight=\paperheight}
\@ifundefined{pdfpageheight}{}{\pdfpageheight=\paperheight}
\textwidth=\paperwidth
\oddsidemargin=2.5cm
\advance\textwidth-2\oddsidemargin
\advance\oddsidemargin-1in
\evensidemargin=\oddsidemargin
\marginparwidth=1.5cm
\marginparsep=.5cm
\parindent=0ex
\parskip=\bigskipamount
\pagestyle{empty}%
%%=====================================================================================
\makeatother

\makeatletter
%%=====================================================================================
%% Switches and helper-macros for triggering error-messages/
%% warnings in case of undefined/multiply defined/changed values
%%=====================================================================================
\newif\if@valueschanged\global\@valueschangedfalse
\newcommand*\@valuesmultiplydefined{}%
\newcommand*\@valuesundefined{}%
\newcommand*\G@valueundefinedtrue{%
  \gdef\@valuesundefined{\@latex@warning@no@line{There were undefined values}}%
}%
%%=====================================================================================
%% \valueundefinederror{<name of value>}
%% Trigger error-message if value is undefined, else do nothing.
%% (Corresponds to \refused of Heiko Oberdiek's refcount-package.)
%%=====================================================================================
\newcommand\valueundefinederror[1]{%
  \@bsphack
  \begingroup
  \@ifundefined{usevalue@#1}{%
    \protect\G@valueundefinedtrue
    \@latex@warning{Value `#1' on page \thepage \space undefined}%
  }{}%
  \endgroup
  \@esphack
}%
%%=====================================================================================
%% \usevalue[{<tokens in case value is undefined>}]{<name of value>}
%%
%% If no optional argument is provided, 
%%   \valueundefinederror{<name of value>}\nfss@text{\reset@font\bfseries??}%
%% is delivered.
%% (Corresponds to \ref of the LaTeX2e-kernel if no optional argument is provided.)
%%=====================================================================================
\@ifdefinable\usevalue{%
  \DeclareRobustCommand\usevalue{\kernel@ifnextchar[{\@usevalue}{\@@usevalue}}%
}%
\@ifdefinable\@@usevalue{%
  \long\def\@@usevalue#1{\usevalueexpandable{#1}{\valueundefinederror{#1}\nfss@text{\reset@font\bfseries??}}}%
}%
\@ifdefinable\@usevalue{\long\def\@usevalue[#1]#2{\usevalueexpandable{#2}{#1}}}%
%%=====================================================================================
%% \usevalueexpandable{<name of value>}{<tokens in case value is undefined>}
%% (Corresponds to \getrefnumber of Heiko Oberdiek's refcount package.)
%%=====================================================================================
\newcommand{\usevalueexpandable}[2]{%
  \@ifundefined{usevalue@#1}{#2}{\csname usevalue@#1\endcsname}%
}%
%%=====================================================================================
%% \immediatedefinevalue{<name of value>}{<tokens to deliver when using the value>}
%% (Almost corresponds to \def\@currentlabel{...}...\label{...} of the LaTeX2e-kernel.
%%  Unlike with \label, the \write is done immediately.)
%%=====================================================================================
\@ifdefinable\immediatedefinevalue{%
  \DeclareRobustCommand{\immediatedefinevalue}{%
    \@definevalue{\immediate\write\@auxout}%
  }%
}%
%%=====================================================================================
%% \definevalue{<name of value>}{<tokens to deliver when using the value>}
%% (Corresponds to \def\@currentlabel{...}...\label{...} of the LaTeX2e-kernel.
%%  Like with \label, the \write is done delayed.)
%=====================================================================================
\@ifdefinable\definevalue{%
  \DeclareRobustCommand{\definevalue}{%
    \@definevalue{\let\thepage\relax\@@definevalue\@auxout}%
   }%
}%
\newcommand\@definevalue[3]{%
    \@bsphack
    \begingroup
    \let\protect\noexpand
    #1{\string\@valuedef{#2}{#3}}%
    \endgroup
    \if@nobreak\ifvmode\nobreak\fi\fi
    \@esphack
}%
\newcommand\@@definevalue[2]{\write#1{\unexpanded\expandafter{\expanded{#2}}}}%
%%%=====================================================================================
%% \@valuedef{<name of value>}
%% (Corresponds to \newlabel of the LaTeX2e-kernel.)
%%%=====================================================================================
\newcommand\@valuedef[1]{\@@valuedef{#1}{usevalue}}%
%%=====================================================================================
%% \@@valuedef{<name of value>}{<prefix for internal macros>}{<tokens to deliver when using the value>}
%% (Corresponds to \@newl@bel / \@testdef of the LaTeX2e-kernel.)
%%=====================================================================================
\newcommand\@@valuedef[3]{%
  \begingroup
  % Hashes were doubled while writing to \@auxout. Let's use a macro for halving them.
  \def\@tempa{#1}%
  \ifx\@newl@bel\@testdef\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
  {%
    \@ifundefined{#2@\@tempa}\relax{%
      \gdef\@valuesmultiplydefined{%
        \@latex@warning@no@line{There were multiply-defined values}%
      }%
      \@latex@warning@no@line{Value `\@tempa' multiply defined}%
    }%
    \global\@namedef{#2@\@tempa}{#3}%
  }{%
    \def\reserved@a{#3}%
    \expandafter\ifx\csname#2@\@tempa\endcsname\reserved@a
    \else
      \global\@valueschangedtrue
    \fi
  }%
  \endgroup
}%
%%=====================================================================================
%% This hooks into \@refundefined (which is executed by \enddocument)
%% for delivering messages in case of undefined/multiply defined/changed values:
%%=====================================================================================
\AtEndDocument{%
  \@ifundefined{@refundefined}\gdef\g@addto@macro\@refundefined{\valueschangedmessage}%
}%
\newcommand\valueschangedmessage{%
  \@valuesundefined
  \if@filesw 
  \ifx\@valuesmultiplydefined\@empty
    \if@valueschanged
      \@latex@warning@no@line{Value(s) may have changed. Rerun to get value(s) right}%
    \fi
  \else
    \@valuesmultiplydefined
  \fi
  \fi
}%
%%=====================================================================================
\makeatother

\begin{document}

\fbox{\parbox{\dimexpr\textwidth-2\fboxsep-2\fboxrule\relax}{Let's see the meaning
of the underlying macro:}}

\makeatletter
\verb|\meaning\usevalue@testerhelloworld|: \texttt{\meaning\usevalue@testerhelloworld}
\makeatother

\fbox{\parbox{\dimexpr\textwidth-2\fboxsep-2\fboxrule\relax}{Now let's use the 
unexpandable variant with the optional argument which by default does trigger
(unexpandable) error-messages in case of the value not being defined/available:}}

\underline{Here \texttt{\string\mysuffix} is not yet defined:}

\verb|\usevalue{testerhelloworld}|: \usevalue{testerhelloworld}

\underline{Here \texttt{\string\mysuffix} is defined:}

\def\mysuffix{helloworld}%
\verb|\def\mysuffix{helloworld}| $\to$ \texttt{\string\mysuffix:\ \meaning\mysuffix}

\verb|\usevalue{tester\mysuffix}|: \usevalue{tester\mysuffix}

\fbox{\parbox{\dimexpr\textwidth-2\fboxsep-2\fboxrule\relax}{Now let's use the
expandable variant which does not trigger (unexpandable) error-messages
in case of the value not being defined/available:}}

Trigger the error-message about the value being undefined ``by hand'' if necessary:

\verb|\valueundefinederror{tester\mysuffix}|
\valueundefinederror{tester\mysuffix}

Perform something within an expansion-context where non-expandable things would disturb:

\verb|Value \texttt{tester\mysuffix} is \ifnum\usevalueexpandable{tester\mysuffix}{-1}=-1 un\fi defined.|:\\
Value {\tt tester\string\mysuffix} is \ifnum\usevalueexpandable{tester\mysuffix}{-1}=-1 un\fi defined.

\fbox{\parbox{\dimexpr\textwidth-2\fboxsep-2\fboxrule\relax}{Here the value \texttt{testerhelloworld} is
defined in terms of \texttt{\string\mysuffix}:}}

\verb|\immediatedefinevalue{tester\mysuffix}{42}|%
\immediatedefinevalue{tester\mysuffix}{42}%

\end{document}

enter image description here

Ulrich Diez
  • 28,770