Ekkart Kleinod schrieb:
> im changes-Paket definiere ich den Befehl "\highlight", nicht sehr
> originell, andere Pakete tun das (leider) auch.
Option 5:
- _Interne_ Makros immer mit einem durch "@" abgetrennten Präfix
versehen, welches den Namespace für die internen Makros Deines
Paketes bildet und so beschaffen ist, dass die Wahrscheinlich-
keit, dass andere Pakete das selbe Präfix benutzen, klein ist.
- Per Paket kvoptions oder ähnliches eine Paketoption
"NamespacePrefix" definieren, die standardmäßig einen leeren
Wert hat, über die sich aber ein Namenspräfix für alle Makros
der _Benutzerebene_ Deines Paketcodes angeben lässt.
- Beim Definieren der Makros der Benutzerebene Deines Pakets erst
prüfen, ob so ein Makro bereits definiert ist.
Wenn ja, Fehlermeldung ausgeben lassen und in der Fehlermeldung
auf die Paketoption "NamespacePrefix" aufmerksam machen,
mittels derer sich das Problem beheben lässt.
- In der Dokumentation darauf aufmerksam machen, dass
Paket-Autoren, die die Benutzerebenenmakros Deines Paket nutzen,
die Benutzerebenenmakros nicht direkt aufrufen sollen, sondern
auf eine Weise, bei der das NamespacePrefix berücksichtigt wird.
Das könnte in etwa so aussehen:
Das unten folgende Demopaket UDFooBar.sty definiert per
\define@key (keyval-Syntax) eine Paketoption "NameSpacePrefix".
Wenn man diese beim Laden des Pakets nicht angibt, werden die
Makros \UserLevelMacroA und \UserLevelMacroB definiert.
Wenn man diese Paketoption angibt, zB
\usepackage[NameSpacePrefix=MYPREFIX]{UDFooBar}
, dann werden diese Makros nicht definiert, sondern stattdessen
die Makros \MYPREFIXUserLevelMacroA und \MYPREFIXUserLevelMacroB.
Interne Makros, also Makros, die der Benutzer nicht direkt
aufrufen soll, haben allesamt Namen, die mit \UDFooBar@...
beginnen.
Die Paketoption NameSpacePrefix definiert das Makro
\UDFooBar@NameSpacePrefix.
Das interne Makro \UDFooBar@NameSpacePrefix hält also das
Namenspräfix für die Makros der Benutzerebene.
Die Kontrollwort-Token, die für die Makros der Benutzerebene
stehen, können durch
\csname\UDFooBar@NameSpacePrefix...\endcsname
gebildet werden.
Also zB
\csname\UDFooBar@NameSpacePrefix UserLevelMacroA\endcsname
und
\csname\UDFooBar@NameSpacePrefix UserLevelMacroB\endcsname
. Angenommen, das Namenspräfix ist leer/\UDFooBar@NameSpacePrefix
ist leer definiert.
Dann ergeben die \csname..\endcsname-Konstrukte die Kontrollwort-
Token
\UserLevelMacroA und \UserLevelMacroB.
Angenommen, das Namenspräfix/das Makro \UDFooBar@NameSpacePrefix
ist so definiert, dass es zu der Phrasse "MYPREFIX" expandiert.
Dann ergeben die \csname..\endcsname-Konstrukte die Kontrollwort-
Token
\MYPREFIXUserLevelMacroA und \MYPREFIXUserLevelMacroB.
Wenn man das entsprechende Kontrollwort-Token per \newcommand
als Makro definieren möchte, muss man es erst aus dem
\csname..\endcsname-Konstrukt bilden lassen, bevor man
\newcommand darauf loslassen kann.
Das könnte so aussehen:
\expandafter\newcommand
\csname\UDFooBar@NameSpacePrefix UserLevelMacroA\endcsname[1]{...}
Auf Dauer nerven solche \expandafter-Orgien aber.
Deshalb ist noch das Makro \UDFooBar@Name definiert, welches so
funktioniert:
\UDFooBar@Name<Zeug ohne geschweifte Klammern>{<MakronameOhnePräfix>}
->
<Zeug ohne geschweifte Klammern>\NameSpacePrefixMakronameOhnePräfix
<Zeug ohne geschweifte Klammern> kann auch leer sein.
\UDFooBar@Name bastelt also aus einem in Klammern
eingeschachtelten Makro-Namen ohne Präfix das entsprechende
Kontrollwort-Token mit Präfix.
Angenommen, das Namenspräfix/das Makro \UDFooBar@NameSpacePrefix
ist so definiert, dass es zu der Phrasse "MYPREFIX" expandiert.
\UDFooBar@Name\newcommand{UserLevelMacroA}...
wird dann zu:
\newcommand\MYPREFIXUserLevelMacroA...
\UDFooBar@Name{UserLevelMacroA}
wird dann zu:
\MYPREFIXUserLevelMacroA
\UDFooBar@Name\show{UserLevelMacroA}
wird dann zu:
\show\MYPREFIXUserLevelMacroA
\UDFooBar@Name\UDFooBar@Name\global\let{UserLevelMacroA}={UserLevelMacroB}
wird dann zu
\UDFooBar@Name\global\let\MYPREFIXUserLevelMacroA={UserLevelMacroB}
wird dann zu
\global\let\MYPREFIXUserLevelMacroA=\MYPREFIXUserLevelMacroB
Damit man es mit den Fehlermeldungen, falls ein bereits
definiertes Benutzerebenen-Makro nochmal definiert werden soll,
leichter hat, ist auch noch ein internes Makro
\UDFooBar@ifdefinable{<NameDesUserLevelMakro>}{<code>}
definiert.
Wenn \MYPREFIXNameDesUserLevelMakro bereits (zB durch ein anderes
Paket) definiert ist, bekommt man eine Fehlermeldung und den
Hinweis, dass man den Name-Clash beheben kann, wenn man das Paket
UDFooBar mit einer anderen Präfix-Angabe in der Paketoption
NameSpacePrefix=...
lädt.
Wenn \MYPREFIXNameDesUserLevelMakro nicht definiert ist, wird
<code> ausgeführt.
Hier das Demo-Paket UDFooBar.sty:
\NeedsTeXFormat{LaTeX2e}[2005/12/01]
\ProvidesPackage{UDFooBar}[2020/02/21 0.00beta1 Demo Package for Namespace-Prefix]
\RequirePackage{kvoptions}
%%--------------------------------------------
%% The hardcoded prefix for all internal macros is "UDFooBar@".
%%--------------------------------------------
%% Namespace-Management for all userlevel-macros:
%%
%% The macro which holds the namespace-prefix for all userlevel-macros:
\newcommand\UDFooBar@NameSpacePrefix{}
%%--------------------------------------------
%% \UDFooBar@Name<stuff without braces>{<MacroName>}
%% ->
%% <stuff without braces>\<\UDFooBar@NameSpacePrefix><MacroName>
%% (In expansion contexts \UDFooBar@Name requires two "hits"
%% by \expandafter/two expansion-steps to deliver the result.)
%--------------------------------------------
\@ifdefinable\UDFooBar@Name{%
\long\def\UDFooBar@Name#1#{\romannumeral0\UDFooBar@Innername{#1}}%
}%
\newcommand\UDFooBar@Innername[2]{%
\expandafter\UDFooBar@Exchange
\expandafter{\csname\UDFooBar@NameSpacePrefix#2\endcsname}{ #1}%
}%
\newcommand\UDFooBar@Exchange[2]{#2#1}%
%%--------------------------------------------
%% \UDFooBar@ifdefinable{<macroname>}{<Code>}
%% Checks whether a control sequence whose name is formed by
%% \UDFooBar@NameSpacePrefix<macroname>
%% is definable.
%% If so, exexutes code.
%% Otherwise raises an error-message.
%%--------------------------------------------
\newcommand\UDFooBar@ifdefinable[1]{%
\UDFooBar@Name\UDFooBar@@ifdefinable{#1}%
}%
\newcommand\UDFooBar@@ifdefinable[2]{%
\edef\reserved@a{\expandafter\@gobble\string#1}%
\@ifundefined\reserved@a{%
\edef\reserved@b{\expandafter\@carcube\reserved@a xxx\@nil}%
\ifx\reserved@b\@qend\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\@firstoftwo}{%
\ifx\reserved@a\@qrelax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}%
}{\@firstoftwo}%
{\UDFooBar@notdefinable}{#2}%
}%
\newcommand\UDFooBar@notdefinable{%
\PackageError{UDFooBar}{%
Currently NameSpacePrefix=`\UDFooBar@NameSpacePrefix' is specified.\MessageBreak
Command \@backslashchar\reserved@a\space already defined.\MessageBreak
Or name \@backslashchar \@qend ... illegal.\MessageBreak
Try specifying a suitable prefix for the\MessageBreak
name-space of the user-macros%
}{%
Try specifying a suitable prefix for the name-space of the\MessageBreak
user-macros of this package via the package-option\MessageBreak
\space\space NameSpacePrefix=<Prefix>\MessageBreak
(Make sure the <Prefix> is not `end' because commands beginning\MessageBreak
with `\@backslashchar\@qend...' are illegal in LaTeX.)%
}%
}%
%%--------------------------------------------
%% The kv-option "NameSpacePrefix":
%%--------------------------------------------
\define@key{UDFooBar}{NameSpacePrefix}{%
\gdef\UDFooBar@NameSpacePrefix{#1}%
\@onelevel@sanitize\UDFooBar@NameSpacePrefix
\ifx\UDFooBar@NameSpacePrefix\@qend
\PackageError{UDFooBar}{%
Name-space-prefix `end' cannot be specified\MessageBreak
and therefore is ignored.\MessageBreak
As default no prefix is used%
}{%
Try specifying a suitable prefix for the name-space of the\MessageBreak
user-macros of this package via the package-option\MessageBreak
\space\space NameSpacePrefix=<Prefix>\MessageBreak
(Make sure the <Prefix> is not `end' because commands beginning\MessageBreak
with `\@backslashchar\@qend...' are illegal in LaTeX.)%
}%
\gdef\UDFooBar@NameSpacePrefix{}%
\fi
}%
\ProcessKeyvalOptions {UDFooBar}
%% Now define all user-level-macros in terms of \UDFooBar@notdefinable
%% and \UDFooBar@Name:
\UDFooBar@ifdefinable{UserLevelMacroA}{%
\UDFooBar@Name\newcommand{UserLevelMacroA}[1]{%
This is the argument of the UDFooBar-package's UserLevelMacroA: #1.%
}%
}%
\UDFooBar@ifdefinable{UserLevelMacroB}{%
\UDFooBar@Name\newcommand{UserLevelMacroB}[1]{%
This is the argument of the UDFooBar-package's UserLevelMacroB: #1.%
}%
}%
%%
%% Note to package programmers:
%% ----------------------------
%%
%% In your packages do never call the user-level-macros of this
%% package directly!
%% Call them via \UDFooBar@Name or via \csname..\endcsname!
%%
%% E.g., do
%%
%% \UDFooBar@Name{UserLevelMacroA}{<Argument>}
%%
%% or
%%
%% \csname\UDFooBar@NameSpacePrefix UserLevelMacroA\endcsname{<Argument>}
%%
%% instead of
%%
%% \UserLevelMacroA{<Argument>}
%%
%% !!! When doing this, be careful about the order in time in which things
%% take place when applying \uppercase/\lowercase and the like !!!
%%
\endinput
% End of UDFooBar.sty
Wenn kein anderes zuvor geladenes Paket eines der Makros
\UserLevelMacroA und \UserLevelMacroB definiert, kann man das
Paket UDFooBar.sty ohne Paket-Optionen laden und bekommt keine
Fehlermeldungen und kann die Makros \UserLevelMacroA und
\UserLevelMacroB verwenden:
\documentclass{article}
\usepackage{UDFooBar}
\begin{document}
\UserLevelMacroA{Argument}
\UserLevelMacroB{Argument}
\end{document}
Angenommen, eines der Makros \UserLevelMacroA / \UserLevelMacroB
ist beim Laden von UDFooBar.sty bereits (durch ein anderes Paket)
definiert.
Dann bekommt man eine nette Fehlermeldung und den Hinweis, dass
man beim Laden von UDFooBar.sty die Paketoption
NameSpacePrefix=...
verwenden kann, um das Problem zu beheben:
\documentclass{article}
\newcommand\UserLevelMacroA{This macro is already defined.}
\usepackage{UDFooBar}
\begin{document}
\UserLevelMacroA{Argument}
\UserLevelMacroB{Argument}
\end{document}
Wenn man das dann tut, werden die Namen der von UDFooBar.sty
definierten Benutzerebenen-Makros allesamt mit einem Präfix
versehen und man kann die bereits von anderen Paketen definierten
Benutzerebenen-Makros unter dem alten Namen weiterbenutzen und
hat die von UDFooBar.sty definierten Benutzerebenen-Makros unter
Makronamen zur Verfügung, die noch das zusätzliche Präfix haben:
\documentclass{article}
\newcommand\UserLevelMacroA{This macro is already defined.}
\usepackage[NameSpacePrefix=MYPREFIX]{UDFooBar}
\begin{document}
\UserLevelMacroA
\MYPREFIXUserLevelMacroA{Argument}
\MYPREFIXUserLevelMacroB{Argument}
\end{document}
Ulrich