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.

Luc
  • 13
  • 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?) – David Carlisle Feb 20 '21 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 '21 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 '21 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. – David Carlisle Feb 20 '21 at 17:02

2 Answers2

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
campa
  • 31,130
  • I'd add \newcommand{\example}{} before doing \def\example#, just for safety. – egreg Feb 20 '21 at 15:04
  • @egreg Good point, I'll edit. – campa Feb 20 '21 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 '21 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 '21 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}

Ulrich Diez
  • 28,770
  • 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 '21 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. ;-) – Ulrich Diez Feb 21 '21 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 '21 at 08: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. – Ulrich Diez Feb 22 '21 at 10:06
  • Oh, that makes sense. Thanks. – campa Feb 22 '21 at 10:09