1

I want to create a command that explicitly requires its user to give the arguments in the curly brackets, or else throw an error.

For instance, take the following command definition:

\newcommand{\example}[1]{The argument is: "#1"}

Now, suppose a user uses it the following way:

\example{Hello}

... will result in:

The argument is: "Hello"

However, if the user uses it like this:

\example Hello

... will result in:

The argument is: "H"ello

This behavior is undesirable for the command I want to define -- how would it be possible to enforce this and return an error to the user? I've tried using ifthen constructs to test the arguments, but a single letter could potentially be a valid argument actually. Using \newcommand* only works if there is whitespace after the command, not if it is used mid-sentence.

4
  • 1
    This will make your command different from almost every other latex construct, won't that be confusing for users? (some primitives like \hbox act this way but no latex commands) (I do not understand your final comment about \newcommand* at all, it doesn't seem true or related to the question?) Feb 20, 2021 at 16:39
  • My concern is mostly that for the command in the vast majority of cases, users want to specify what are the arguments exactly, and if they forget explicitly stating one of the multiple arguments, that it raises an error. As in, the automatic absorption of a character to complement "missing" arguments is likely undesired behavior. The \newcommand* enforces at least that the command does not take whitespace as the absorbed 'missing' argument, e.g., if it used it like \example [line break] it does not allow the [line break] character to be the argument it absorbs and throws an error.
    – Luc
    Feb 20, 2021 at 16:59
  • I agree that it is different from every other latex construct -- ideally I would like to add a custom error message to state why compilation fails on this particular command.
    – Luc
    Feb 20, 2021 at 17:01
  • the * form of \newcommand makes the definition non \long so it does not accept a paragraph break in the argument, it has no effect on single line breaks or brace handling. Feb 20, 2021 at 17:02

2 Answers 2

2

You can "grab # as macro argument" (see Macros with # as the last parameter and "Grab to #{" macro arguments for explanation)

\documentclass[11pt]{article}

\newcommand*{\example}{}% test if undefined; better safe than sorry
\def\example#{\exampleaux}
\newcommand{\exampleaux}[1]{The argument is: "#1"}

\begin{document}

\example{Hello}

\example Hello

\end{document}

The second use will break down with the error

! Use of \example doesn't match its definition.
l.11 \example H
               ello
4
  • I'd add \newcommand{\example}{} before doing \def\example#, just for safety.
    – egreg
    Feb 20, 2021 at 15:04
  • @egreg Good point, I'll edit.
    – campa
    Feb 20, 2021 at 15:04
  • Thank you for the answer. One follow-up I had: how would it generalize to 2 or more argument, or with an optional argument in it? E.g., \def\example#{\exampleaux} \def\exampleaux#{\exampleauxtwo} \newcommand{\exampleauxtwo}[2]{The argument is: "#1" and "#2"} Does not yield the expected result, as now \example{abc} def is still allowed.
    – Luc
    Feb 20, 2021 at 15:11
  • @Luc Basically you have to repeat the game. You leave \def\example#{\exampleaux} but then you define \exampleaux to store its argument in some macro, and then execute another macro defined with the # as last parameter, which again calls the final macro which does the job. Can't edit now as I have to leave; later or tomorrow...
    – campa
    Feb 20, 2021 at 15:19
1

In the example below the macros \example␣␣ and \fetchbracedarg form a loop for accumulating brace-nested arguments in the token-register \toks@ in an amount which corresponds to the initial value of the count-register \@tempcnta—I suggest a token-register instead of a scratch-macro in order to avoid the halving of amounts of consecutive hashes (###) which takes place with hashes occuring in ⟨definition text⟩s of macros at the time of expanding the macros in question.

\documentclass{article}

\makeatletter
\newcommand\example{%
  \toks@\expandafter{%
        \expandafter\toks@
        \expandafter{\the\expandafter\toks@\expandafter}%
        \expandafter\@tempcnta\expandafter=\the\@tempcnta\relax
        \innerexample
  }%
  \@tempcnta=3 %
  \csname example\@firstofone{ } \endcsname
}%
\expandafter\@ifdefinable\csname example\@firstofone{ } \endcsname{%
  \@namedef{example\@firstofone{ } }#{\fetchbracedarg}%
}%
\newcommand\fetchbracedarg[1]{%
  \toks@\expandafter{\the\toks@{#1}}%
  \advance\@tempcnta -1 %
  \ifnum\@tempcnta=0 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\the\toks@}{\csname example\@firstofone{ } \endcsname}%
}%
\newcommand\innerexample[3]{%
  \par This is argument 1: "#1"%
  \par This is argument 2: "#2"%
  \par This is argument 3: "#3"%
}%
\makeatother

\begin{document}

\example{A}{B}{C}

% \example{A}{B}C % Triggers error: ! Use of \example   doesn't match its definition.

% \example{A}B{C} % Triggers error: ! Use of \example   doesn't match its definition.

% \example A{B}{C} % Triggers error: ! Use of \example   doesn't match its definition.

\end{document}
5
  • Very nice! One thing I don't really get: why the \@firtstofone{ } construction? (I'm on smartphone so I can't play with the code right now.)
    – campa
    Feb 21, 2021 at 13:48
  • @campa \@firstofone just delivers its argument (with surrounding braces removed if present). Here \@firstofone{␣}␣ is just a silly trick for obtaining two consecutive spaces: The space which is the argument of \@firstofone and the space which is behind the closing-brace of the argument of \@firstofone. This way both the \csname..\endcsname-construct and the \@namedef-mechanism deliver the control-word-token \example␣␣ whose name might occur in error-messages and looks very similar to \example. ;-) Feb 21, 2021 at 14:09
  • Sorry, I wasn't clear, I know what \@firstofone is :-), I meant why this construct instead of something easier like example@ or ex@mple (the latter being perverse, I know...).
    – campa
    Feb 22, 2021 at 8:21
  • @campa I know that you know these things, but I thought I'd better explain in case someone else reads the comments, too. In case a "...doesn't match its definition"-error is raised due to missing braces I wanted the macro-name displayed to look as similar to the phrase \example as possible. I decided not to use \example␣ (a single trailing space in the macro-name) but to use \example␣␣ (two trailing spaces in the macro-name) because LaTeX's \DeclareRobustCommand internally defines macros with a single trailing space appended to the macro-name. Feb 22, 2021 at 10:06
  • Oh, that makes sense. Thanks.
    – campa
    Feb 22, 2021 at 10:09

You must log in to answer this question.

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