4

Is there a version of \csdef{}{} that reports an error if the value is already defined? The MWE below compiles fine since no checking for the macro already being defined is done and yields:

enter image description here

I want to replace the csdef such that it reports an error condition similar to what a duplicated \newcommand would.

Code:

\documentclass{article}
\usepackage{etoolbox}

\newcommand*{\FooValue}{First Value}
\csdef{FooValue}{Second Value}

\csdef{Some Macro}{First Value}
\csdef{Some Macro}{Second Value}

\begin{document}
  FooValue=\csuse{FooValue}

  Some Macro=\csuse{Some Macro}
\end{document}
1

2 Answers 2

5

I don't think there is such a macro in etoolbox. Its \cs..def... macros all mirror what you can do with \def and \def has no check to see if it overrides anything either.

Here are two possible solutions. One is basically \csdef with a test, the other is \newcommand with \csname ...\endcsname (so \necommand does the test).

In the \csdef with a test I introduced a \csdef to a throwaway macro (\my@etb@throwaway), so I wouldn't have to collect and gobble the possible arguments of \csdef myself. (Of course that means you shouldn't use (\my@etb@throwaway anywhere.)

The following MWE

\documentclass{article}
\usepackage{etoolbox}

\newcommand*{\FooValue}{First Value}
\csdef{FooValue}{Second Value}

\csdef{Some Macro}{First Value}
\csdef{Some Macro}{Second Value}

\newcommand*{\csdefifundef}[1]{%
  \ifcsundef{#1}
    {\csdef{#1}}
    {\PackageError{etoolbox}
      {Command sequence '#1' already defined}
      {You can only define undefined commands with \string\csdefifundef}%
     \csdef{my@etb@throwaway}}}

\csdefifundef{Some Other Macro}{First Value}
\csdefifundef{Some Other Macro}{Second Value}


\newcommand*{\csnewcommand}[1]{%
  \expandafter\newcommand\expandafter{\csname #1\endcsname}}

\csnewcommand{Some Other Different Macro}{First Value}
\csnewcommand{Some Other Different Macro}{Second Value}

\begin{document}
  FooValue=\csuse{FooValue}

  Some Macro=\csuse{Some Macro}

  FooValue=\csuse{FooValue}

  Some Other Macro=\csuse{Some Other Macro}

  Some Other Different Macro=\csuse{Some Other Different Macro}
\end{document}

throws the errors

! Package etoolbox Error: Command sequence 'Some Other Macro' already defined.

See the etoolbox package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.18 \csdefifundef{Some Other Macro}{Second Value}

? 

! LaTeX Error: Command \Some Other Different Macro already defined.
               Or name \end... illegal, see p.192 of the manual.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.25 ...{Some Other Different Macro}{Second Value}

? 

and produces the output

FooValue=Second Value//Some Macro=Second Value//FooValue=Second Value//Some Other Macro=First Value//Some Other Different Macro=First Value

when we force compilation despite the errors.

2

The TeXbook explains a nice feature when it comes to defining macros: In the ⟨parameter text⟩ of a macro-definition you can use #{-notation for a macro whose last argument will be delimited by a left curly brace { which—unlike other argument-delimiters—will be re-inserted as if it had been left in place.

Thus I sometimes use a macro-mechanism \NameToCs which processes an argument which is delimited by a left curly brace ({) and another argument which is nested in curly braces.

The argument nested in curly braces is taken for the name of a ⟨control sequence token⟩ which is to be constructed via \csname..\endcsname.

\NameToCs works as follows:

\NameToCs⟨stuff not in curly braces⟩{NameOfCs}
→
⟨stuff not in curly braces⟩\NameOfCs

(If you wish to obtain only the control-sequence-token \NameOfCs, then you can leave ⟨stuff not in curly braces⟩ empty: \NameToCs{NameOfCs} → \NameOfCs)

\makeatletter
\newcommand\exchange[2]{#2#1}%
\@ifdefinable\NameToCs{\long\def\NameToCs#1#{\romannumeral0\innerNameToCs{#1}}}%
\newcommand\innerNameToCs[2]{\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}}%
\makeatother

There are various uses for such a macro:

  1. \NameToCs{foo}\foo
  2. \NameToCs\string{foo}\string\foo
  3. \NameToCs\meaning{foo}\meaning\foo
  4. \NameToCs\global\long\def{foo}...\global\long\def\foo...
  5. \NameToCs\newcommand*{foo}...\newcommand*\foo...
  6. \NameToCs\NameToCs\global\let{foo}={bar}\NameToCs\global\let\foo={bar}\global\let\foo=\bar

Usage-example 5 can be applied within the ⟨balanced text⟩ of the definition of a macro \csnewcommand:

\newcommand*\csnewcommand{\NameToCs\newcommand}%

Putting the pieces together in a MWE:

\documentclass{article}

% Define the macro \NameToCs:
\makeatletter
\newcommand\exchange[2]{#2#1}%
\@ifdefinable\NameToCs{\long\def\NameToCs#1#{\romannumeral0\innerNameToCs{#1}}}%
\newcommand\innerNameToCs[2]{\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}}%
\makeatother

% Define \csnewcommand and \csrenewcommand and \csProvideCommand and \csDeclareRobustCommand
\newcommand*\csnewcommand{\NameToCs\newcommand}%
\newcommand*\csrenewcommand{\NameToCs\renewcommand}%
\newcommand*\csProvideCommand{\NameToCs\ProvideCommand}%
\newcommand*\csDeclareRobustCommand{\NameToCs\DeclareRobustCommand}%

\csnewcommand*{FooValue}{FooValue's First Value}
% This throws an error:
%\csnewcommand*{FooValue}{FooValue's Second Value}
% This does not throw an error.
%\csrenewcommand*{FooValue}{FooValue's Second Value}

\csnewcommand*{Some Macro}{Some Macro's First Value}
% This throws an error:
%\csnewcommand*{Some Macro}{Some Macro's Second Value}
% This does not throw an error.
%\csrenewcommand*{Some Macro}{Some Macro's Second Value}

\csnewcommand*{Some Other Macro}{Some Other Macro's First Value}
% This throws an error:
%\csnewcommand*{Some Other Macro}{Some Other Macro's Second Value}
% This does not throw an error.
\csrenewcommand*{Some Other Macro}{Some Other Macro's Second Value}

\begin{document}

\noindent
\texttt{\NameToCs\string{FooValue}} is defined as \texttt{\NameToCs\meaning{FooValue}}\\
Expanding \texttt{\NameToCs\string{FooValue}} yields: \NameToCs{FooValue}

\bigskip

\noindent
\texttt{\NameToCs\string{Some Macro}} is defined as \texttt{\NameToCs\meaning{Some Macro}}\\
Expanding \texttt{\NameToCs\string{Some Macro}} yields: \NameToCs{Some Macro}

\bigskip

\noindent
\texttt{\NameToCs\string{Some Other Macro}} is defined as \texttt{\NameToCs\meaning{Some Other Macro}}\\
Expanding \texttt{\NameToCs\string{Some Other Macro}} yields: \NameToCs{Some Other Macro}

\end{document}

enter image description here

You must log in to answer this question.

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