9

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?

4
  • 3
    Do you mean \def?
    – jon
    Feb 18, 2015 at 18:13
  • @jon: Does \def somehow allow to use optional arguments as well?
    – bluenote10
    Feb 18, 2015 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, 2015 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, 2015 at 18:18

3 Answers 3

9

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.

6
  • 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, 2015 at 18:18
  • @JosephWright It would be easy to comply with the optional *, but the idea is wrong to begin with!
    – egreg
    Feb 18, 2015 at 18:19
  • Thanks. Took me a moment to see how it works. Nice trick!
    – bluenote10
    Feb 18, 2015 at 18:25
  • this looks evil. hope i never have to debug a journal submission that uses it. Feb 18, 2015 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, 2015 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}

You must log in to answer this question.

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