7

In some situations I wish there was a way to define a command \tmp so that

  • if \tmp does not exist: Defined \tmp
  • if \tmp does exits: Redefine \tmp

My current approach is simply to use either \newcommand or \renewcommand. However, this means that I often have to change from one version to the other if I reorder my documents and sometimes limits the overall reusability of my code.

In this question I learned about \providecommand, which almost solves my problem: It can be used irrespective of whether \tmp is defined, but it only defines on the first occurrence and does not overwrite. This lead me to the naive attempt:

\newcommand{\overwritecommand}[2]{
  \providecommand{#1}{#2}
  \renewcommand{#1}{#2}
}

However, this approach is obviously not general enough:

% it works for
\overwritecommand{\tmp}{test}

% but not for commands with arguments like
\overwritecommand{\tmp}[1]{test: #1} 
% Error: You can't use `macro parameter character #' in horizontal mode.

Is there any other way to achive the define or overwrite behavior?

  • 3
    Do you mean \def? – jon Feb 18 '15 at 18:13
  • @jon: Does \def somehow allow to use optional arguments as well? – bluenote10 Feb 18 '15 at 18:15
  • \newcommand{\declarecommand}[1]{\providecommand{#1}{}\renewcommand{#1}} But, for your own safety, don't use it! Something like \declarecommand{\box}[1]{\fbox{#1} would be funnily disastrous. – egreg Feb 18 '15 at 18:15
  • It can be used that way. The package xparse has \DeclareDocumentCommand, which is much easier to use; there's also various \def-related commands in the package etoolbox. – jon Feb 18 '15 at 18:18
8

Yes, there is a method:

\newcommand{\declarecommand}[1]{\providecommand{#1}{}\renewcommand{#1}}

Why does it work? Because TeX uses macro expansion and it's irrelevant what \providecommand defines #1 to be, if #1 wasn't defined, because you redefine it immediately.

Now that you know how to do it, try

\declarecommand{\box}[1]{\fbox{#1}}

and enjoy the wreck!

There is a reason why LaTeX doesn't provide a \declarecommand function: you MUST be aware whether you are redefining an existent command.

If you want to allow the optional *, then

\makeatletter
\newcommand\declarecommand{\@star@or@long\@declarecommand}
\newcommand\@declarecommand[1]{%
  \provide@command{#1}{}%
  \renew@command{#1}%
}
\makeatother

will do.

  • Drat: I was just posting more or less the same thing but was held up thinking about the optional star and whether to handle it! – Joseph Wright Feb 18 '15 at 18:18
  • @JosephWright It would be easy to comply with the optional *, but the idea is wrong to begin with! – egreg Feb 18 '15 at 18:19
  • Thanks. Took me a moment to see how it works. Nice trick! – bluenote10 Feb 18 '15 at 18:25
  • this looks evil. hope i never have to debug a journal submission that uses it. – barbara beeton Feb 18 '15 at 18:28
  • @barbarabeeton I hope I made clear why one shouldn't use it. There are several ways to shoot one's own foot, this one is quite funny. – egreg Feb 18 '15 at 19:01
3

I understand that you need \def but with parameters like \newcommand. It is possible to define our own\newcommand which ignores if the defined control sequence has a meaning. For example we can use the code from this page:

\def\newcommand#1{\isnextchar[{\newcommandA#1}{\newcommandA#1[0]}}
\def\newcommandA#1[#2]{\edef\tmpp{\ifcase#2%
   \or1\or12\or123\or1234\or12345\or123456\or1234567\or12345678\or123456789\fi}%
   \edef\tmpp{\expandafter\addhashs\tmpp.}%
   \isnextchar[{\newcommandB#1}{\long\expandafter\def\expandafter#1\tmpp}%
}
\def\newcommandB#1[#2]{%
   \def#1{\isnextchar[{\runcommand#1}{\runcommand#1[#2]}}%
   \long\expandafter\def\csname\string#1X\expandafter\endcsname\tmpp
}
\def\addhashs#1{\ifx.#1\else #####1\expandafter\addhashs\fi}
\long\def\runcommand#1[#2]{\csname\string#1X\endcsname{#2}} 
1

Since the classic examples have already been shown, I'll just add the xparse way, which is quite user-friendly:

\documentclass{article}
\usepackage{xparse}
\DeclareDocumentCommand{\foo}{m}
 {Foo: #1}
\begin{document}

\foo{bar}

\DeclareDocumentCommand{\foo}{om}
 {\IfNoValueTF{#1}%
   {Bar: no optional, just #2}%
   {Bar: optional = #1, plus #2}%
 }%

\foo{Baz}

\foo[Bar]{Baz}

\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.