Eu tenho o seguinte pacote
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypkg}
\providecommand*\@mycmd{No {\MakeUppercase Mycmd} defined}
\newcommand*\Mycmd[1]{\renewcommand*\@mycmd{#1}}
\newcommand*\showDF[1]{\csname @#1\endcsname}
\endinput
Ele fornece ao usuário o comando \Mycmd{}
para renovar \@mycmd
e \showDF{mycmd}
mostrar seu conteúdo:
\documentclass[a4paper,titlepage,11pt,twoside,openright]{report}
\usepackage{mypkg}
\begin{document}
\showDF{mycmd}\par
\Mycmd{Test}
\showDF{mycmd}
\end{document}
Gostaria que os dois primeiros comandos fossem definidos com uma macro, para gerar vários pares de cmd.
\@newDF{mycmd}{Mycmd}
Eu tentei com
\newcommand*\@newDF[2]{
\providecommand*\csname @#1\endcsname{No {\MakeUppercase #2} defined}
\newcommand*\csname #2\endcsname[1]{\renewcommand\csname @#1\endcsname{##1}}
}
mas não funcionará.
\newcommand*\newDF[2]{
\expandafter\providecommand\expandafter*\csname @#1\endcsname{No {\MakeUppercase #2} defined}
\expandafter\newcommand\expandafter*\csname #2\endcsname[1]{\renewcommand\csname @#1\endcsname{##1}}
}
compila, mas realmente não sei por que os coloquei \expandafter
lá e, de qualquer forma, \MakeUppercase
é ignorado quando \showDF{mycmd}
executado e \Mycmd{Hi}
não tem efeito. Substituir \csname @#1\endcsname
por um @nameuse{@#1}
ou \csuse{@#1}
nenhum funciona. o que estou perdendo?
Responder1
Resumidamente, a razão pela qual o seu uso \expandafter
falha é que você não tem o suficiente deles. Para obter mais detalhes, consulte as explicações eloqüentes sobre isso em outras partes deste site, comopor que precisamos de onze expansões para expandir quatro tokens na ordem certa.
O problema que você está enfrentando \MakeUppercase
é porque ele não é expansível, o que significa que você não pode usá-lo diretamente dentro de uma \csname...\endcsname
definição. Isso é explicado em detalhes nas respostas da postagem
crie um macro token maiúsculo usando csname.
Werner deu uma solução onde evita o problema de letras maiúsculas, passando o nome maiúsculo e não maiúsculo para o criador da macro. Aqui está outra solução que coloca o nome da macro em maiúsculas automaticamente.
Em vez de usar \providescommand
para verificar se o \@my<command>
já foi definido, verifico isso diretamente usando, \ifcsdef
pois o uso \providescommand
exigiria alguns \expandafter
s que eu preferiria evitar. Finalmente, como bônus, o código abaixo também define uma “função de fábrica” \@DefineMyCommands
para criar uma família de tais comandos a partir de uma lista separada por vírgulas.
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand\@DefineMyCommand[1]{\@ReallyDefineMyCommand#1\relax}
\def\@ReallyDefineMyCommand#1#2\relax{%
% Define command <#1#2> to set @my#1#2={arg of <#1#2>}
% Here #1 is the first character of the comamnd and #2 is the rest.
% We have isolated #1 so that we can uppercase it on the next line
\uppercase{\expandafter\gdef\csname #1}#2\endcsname##1{\@namedef{@my#1#2}{##1}}
% if \@my#1#2 is not already defined then define it
\ifcsdef{@my#1#2}{\relax}{\@namedef{@my#1#2}{No \MakeUppercase{#1#2} defined!}}
}
\newcommand{\@DefineMyCommands}{\forcsvlist{\@DefineMyCommand}}% factory function
\@DefineMyCommands{mycmd, test, fred, julie}% define a bunch of macros
\makeatother
\newcommand*\showDF[1]{\csname @my#1\endcsname}
\begin{document}
\showDF{mycmd}
\Mycmd{A command!}
\showDF{mycmd}
\showDF{test}
\Test{A test!}
\showDF{test}
\end{document}
Aqui está a saída "esperada":
EditarNa verdade, definir um comando de fábrica é um pouco bobo se for usado apenas uma vez. A menos que haja um grande número destas macros, seria mais inteligente escrever
\forcsvlist{\@DefineMyCommand}{mycmd, test, fred, julie}
pois isso aplica o comando \@DefineMyCommand
aos elementos da lista sem criar uma macro desnecessária.
Responder2
Aqui está como você pode fazer isso:
\documentclass{article}
\makeatletter
%\providecommand*\@mycmd{No {\MakeUppercase Mycmd} defined}
%\newcommand*\Mycmd[1]{\renewcommand*\@mycmd{#1}}
\newcommand{\@newDF}[2]{%
\@namedef{@#1}{No \MakeUppercase{#2} defined}%
%\expandafter\newcommand\csname @#1\endcsname{No \MakeUppercase{#2} defined}% Alternative to above
\expandafter\newcommand\csname #2\endcsname[1]{%
\expandafter\renewcommand\csname @#1\endcsname{##1}}
}
\@newDF{mycmd}{Mycmd}
\newcommand*\showDF[1]{\csname @#1\endcsname}
\makeatother
\begin{document}
\showDF{mycmd}\par
\Mycmd{Test}
\showDF{mycmd}
\end{document}
A saída é:
Nenhum
teste definido pelo MYCMD
Oas definições são dadas com um\show
:
> \@mycmd=macro:
->No \MakeUppercase {Mycmd} defined.
> \Mycmd=\long macro:
#1->\expandafter \renewcommand \csname @mycmd\endcsname {#1}.