5

How can I write transparent wrapper around xdef that, depending on package options, report error, override or obey previous definition without sacrificing all flexibility? I have no idea how to pass all this # in.

EDIT: Example what I can do and what I want to do:

%% This is not valid. xdef have different syntax.
%% \ifpackage@force\xdef\package@define\xdef\fi
\ifpackage@obey\xdef\package@define\providecommand\fi
\ifpackage@err\xdef\package@define\newcommand\fi

So I can have generic interface for providecommand and newcommand. I want add checking in xdef.

  • What do you want to achieve? A package? – Marco Daniel May 30 '13 at 19:53
  • Yes. Package with options, controlling what to do with conflicts. – KAction May 30 '13 at 20:04
  • 1
    Maybe you should provide a minimal working example to demonstrate your issue. – Marco Daniel May 30 '13 at 20:17
  • Do you mean similar to \newcommand in the sense of checking for an existing definition, etc.? – Joseph Wright May 30 '13 at 20:23
  • 3
    you could define a command like \newcommand or \renewcommand but which used \xdef rather than \def internally, but it would be a bad idea to redefine \xdef to add checking capability, it would almost certainly break latex. – David Carlisle May 30 '13 at 20:28
9

something like this

\begin{filecontents}{cxdef.sty}
\ProvidesPackage{cxdef}[2013/05/30]

\DeclareOption{over}{\let\myxdef\xdef}
\DeclareOption{error}{\let\myxdef\errorxdef}
\DeclareOption{preserve}{\let\myxdef\preservexdef}


\def\errorxdef#1{%
\ifx#1\@undefined
  \expandafter\xdef
\else
  \PackageError{cxdef}{\string#1 already defined}{think of a new name}%
  \expandafter\skip@xdef
\fi
#1}


\def\preservexdef#1{%
\ifx#1\@undefined
  \expandafter\xdef
\else
  \expandafter\skip@xdef
\fi
#1}

\def\skip@xdef#1#{\@gobble}

\ExecuteOptions{over}

\ProcessOptions\relax

\end{filecontents}

\documentclass{article}

\usepackage[error]{cxdef}

\myxdef\mbox#1#2\zz{this is bad}

\myxdef\zzzz#1#2\zz{this is good}

\typeout{\meaning\zzzz}


\stop

which produces

! Package cxdef Error: \mbox already defined.

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

l.39 \myxdef\mbox
                 #1#2\zz{this is bad}
? 
macro:#1#2\zz ->this is good
 )

\xdef, \errorxdef and \preservexdef are available or you can use \myxdef which is \let to one of those three depending on a package option.

As egreg noted in a comment on the question, it would be better to use \protected@xdef rather than \xdef wherever the latter appears in the package code, but I'll leave as \xdef for now as that was the original question.

|improve this answer|||||
5

This answer tries to answer the question, if \xdef can be replaced by a macro to catch the command to be defined.

The syntax of \xdef is quite complicate, the relevant aspects:

  1. \xdef is unexpandable.
  2. The definition can be prefixed by \long, \outer, and e-TeX adds \protected.
  3. \afterassignment might insert a token right after the definition.

Vanilla TeX already fails issue 1. At least e-TeX provides an approximation, if the new macro is defined using \protected.

Issue 2 is tried by the following example for various formats with e-TeX (etex, pdflatex):

\catcode`\@=11 % same as \makeatletter
\def\msg#{\immediate\write16}% short for printing something on the console

\let\xdef@original\xdef

\def\xdef@test@{}
\long\def\xdef@test@L{}
\protected\def\xdef@test@P{}
\protected\long\def\xdef@test@PL{}

\outer\def\xdef@test@O{}
\long\outer\def\xdef@test@LO{}
\protected\outer\def\xdef@test@PO{}
\protected\long\outer\def\xdef@test@PLO{}

\protected\def\xdef{%
  \def\xdef@test{}%
  \edef\xdef@prefix{%
    \ifx     \xdef@test\xdef@test@
    \else\ifx\xdef@test\xdef@test@L
      \noexpand\long
    \else\ifx\xdef@test\xdef@test@P
      \noexpand\protected
    \else\ifx\xdef@test\xdef@test@PL
      \noexpand\protected
      \noexpand\long
    \else
      \xdef@test@outer
    \fi\fi\fi\fi
  }%
  \xdef@check
}

\def\xdef@test@outer{%
  \expandafter\ifx     \csname xdef@test\expandafter\endcsname
                       \csname xdef@test@O\endcsname
    \noexpand\outer
  \else\expandafter\ifx\csname xdef@test\expandafter\endcsname
                       \csname xdef@test@LO\endcsname
    \noexpand\long
    \noexpand\outer
  \else\expandafter\ifx\csname xdef@test\expandafter\endcsname
                       \csname xdef@test@PO\endcsname
    \noexpand\protected
    \noexpand\outer
  \else\expandafter\ifx\csname xdef@test\expandafter\endcsname
                       \csname xdef@test@PLO\endcsname
    \noexpand\protected
    \noexpand\long
    \noexpand\outer
  \fi\fi\fi\fi
}

\edef\xdef@test@undefined{\meaning\xdef@undefined}
\edef\xdef@test@relax{\meaning\relax}
\def\xdef@check#1{%
  \edef\xdef@test{\meaning#1}%
  \ifx\xdef@test\xdef@test@undefined
    \chardef\xdef@ok=1 %
  \else\ifx\xdef@test\xdef@test@relax
    \chardef\xdef@ok=1 %
  \else
    \chardef\xdef@ok=0 %
  \fi\fi
  \ifcase\xdef@ok
    \errmessage{\string#1 is already defined}%
    \expandafter\def\expandafter\xdef@dummy   
  \or
    \expandafter\xdef@prefix
    \expandafter\xdef@original\expandafter#1%
  \fi
}

% testing
\xdef\aaa{}
\long\xdef\bbb{}
\protected\xdef\ccc{}
\protected\long\xdef\ddd{}
\outer\xdef\eee{}
\long\outer\xdef\fff{}
\protected\outer\xdef\ggg{}
\protected\long\outer\xdef\hhh{}
\xdef\iii{}

% end of job (LaTeX or plain TeX)
\csname @@end\endcsname\end

The prefixes can only be caught another definition, therefore the new \xdef starts with the definition of \xdef@test. Then the new definition is tested against a series of other macros to find the prefixes that was active. Prefix \outer complicates the testing because of its nature. There are restrictions, where \outer macros can be used. The found prefixes are stored in \xdef@prefix.

Then the new command token is tested, if it is undefined or has the meaning of \relax (LaTeX's interpretation of "undefined" in \@ifundefined). This is checked via \meaning. An error is thrown, if the new command is already defined and a dummy command \xdef@dummy is defined instead.

However this redefined \xdef is NOT a valid replacement for the original \xdef. It fails issue 3, the \afterassignment feature:

  • Expandable stuff between the prefixes and the final definition does not hurt. Thus we could test for the command to be defined. But throwing an error message (or printing a warning) is not expandable and TeX complains:

    ! You can't use a prefix with `\errmessage'.
    

    Therefore we need at least one test definition to catch the prefixes.

  • If \afterassignment is in use, then the token is inserted right after the first test definition, way to early:

    • The inserted token might want to use the new definition, that is not yet defined.
    • The definition business is not finished. The token might want to clean up some stuff and mess up things.

Thus I am pretty sure, that there is no way with TeX or e-TeX that can simulate all aspects of \xdef by a macro that catches the command to be defined to print warnings or to throw errors, if the command is already defined.

|improve this answer|||||

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.