3

I have a macro which takes two arguments, a name and the code:

\newcommand\definesomething[2]{%
  \@namedef{something@#1}##1{#2}}
\definesomething{whatever}{Code with #1}
\show\something@whatever

This works fine, but I would like to change the inner definition so a macro is defined which defines a macro using the given code:

\newcommand\definesomething[2]{%
  \@namedef{something@#1}{%
    % ...
    \def\something##1{#2}%
    % ...
  }}
\definesomething{whatever}{Code with #1}
\show\something@whatever

Obviously it does not work this way. But how to do this correctly (without using xparse)?

Of course I could use a helper macro here, but IMHO this is far from being a good solution:

\newcommand\definesomething[2]{%
  \@namedef{something@@#1}##1{#2}% define helper macro
  \@namedef{something@#1}{%
    % ...
    \expandafter\let\expandafter\something\csname something@@#1\endcsname
    % ...
  }}
\definesomething{whatever}{Code with #1}
\show\something@whatever

Any idea how to do better?

Addendum 2019-09-14: The user interface (\definesomething{whatever}{Code with #1}) needs to remain the same, and I would like to get rid of the extra helper macro \@namedef{something@@#1} which is defined ob every usage of \definesomething. It would be nice if only \something@whatever whould be defined after usage of \definesomething{whatever}{...}.

  • 1
    Traditional TeX-engines don't provide means for doubling hashes outside \edef/\xdef-contexts. In \edef/\xdef-contexts both during \the-expansion of token-parameters/token-registers and during delivering the balanced text of \unexpanded hash-doubling takes place. This is for compensating that the amount of hashes will be halved when expanding the macro that was defined in terms of \edef/\xdef. Unexpanded-writing (also with \scantokens' pseudo-files) also doubles hashes. But reading the (pseudo)-file requires re-tokenization. Hereby catcode-régimes might cause problems. – Ulrich Diez Sep 14 '19 at 10:47
3

If ε-TeX extensions are available, then you can systematically nest \unexpanded inside \edef.

You can achieve the sequence

\definesomething{⟨MacroNamePart⟩}{⟨Code with #1⟩} → define \something@MacroNamePart

\something@MacroNamePart → define \something{#1} → Code with #1

as follows:

\makeatletter
\newcommand\definesomething[2]{%
  \expandafter\edef\csname something@#1\endcsname{%
    \unexpanded{\def\something}####1\unexpanded{{#2}}%
  }%
}%

\show\definesomething
\definesomething{whatever}{Code with #1}%

\show\something@whatever
\something@whatever

\show\something

\stop

enter image description here

Each definition-level where the hashes of the parameter of the user-level-macro shall be doubled (so that they can be halved during expansion of the macro) requires another nesting-level of \unexpanded.

As a rule of thumb the only things that must not go into \unexpanded are the hashes-and-digit-sequences that denote arguments that occur in the ⟨parameter texts⟩ of those macros that get defined via \edef.

The braces wherein the ⟨balanced texts⟩ of the ⟨definition texts⟩ of these \edef-definitions are nested also need not to go into \unexpanded which makes life easy.

You can easily achieve the sequence

\definesomething{⟨MacroNamePart⟩}{⟨Code with #1⟩} → define \something@DefinitionNestLevelOne

\something@DefinitionNestLevelOne → define \something@DefinitionNestLevelTwo

\something@DefinitionNestLevelTwo → define \something@MacroNamePart

\something@MacroNamePart → define \something{#1} → Code with #1

as follows:

\makeatletter
\newcommand\definesomething[2]{% 
  \edef\something@DefinitionNestLevelOne{%<-opening brace of \edef-definition-text needs not be in \unexpanded
  % here things are surrounded by 1 \edef, thus everything that is not a parameter
  % of this one \edef must be nested in 1 level of \unexpanded:
    \unexpanded{\edef\something@DefinitionNestLevelTwo}{%<-opening brace of \edef-definition-text needs not be in \unexpanded
     % here things are surrounded by 2 \edef, thus everything that is not a parameter
     % of one of these 2 \edef must be nested in 2 levels of \unexpanded:
      \unexpanded{\unexpanded{\expandafter\edef\csname something@#1\endcsname}}{%<-opening brace of \edef-definition-text needs not be in \unexpanded
         % here things are surrounded by 3 \edef, thus everything that is not a parameter
         % of one of these 3 \edef must be nested in 3 levels of \unexpanded:
         \unexpanded{\unexpanded{\unexpanded{\def\something}}}################1\unexpanded{\unexpanded{\unexpanded{{#2}}}}% 
      }%<-closing brace of \edef-definition-text needs not be in \unexpanded
    }%<-closing brace of \edef-definition-text needs not be in \unexpanded
  }%<-closing brace of \edef-definition-text needs not be in \unexpanded
}%

\show\definesomething
\definesomething{whatever}{Code with #1}%

\show\something@DefinitionNestLevelOne
\something@DefinitionNestLevelOne

\show\something@DefinitionNestLevelTwo
\something@DefinitionNestLevelTwo

\show\something@whatever
\something@whatever

\show\something

\stop

enter image description here


What can you do if ε-TeX-extensions are not available?

In the very special case that ⟨Code with #1⟩ does only contain #1 but not ##1 etc, you can use ⟨Code with #1⟩ for defining a macro which processes one argument, and for multiplying the hashes call that macro with multiple hashes as argument. Could look like this:

\makeatletter
\newcommand\definesomething[2]{%
  {%
    \def\tempa##1{\def\tempa{#2}}%
    \tempa{########1}%
    \expandafter
  }\expandafter\innerdefinesomething\expandafter{\tempa}{#1}%
}%

\newcommand\innerdefinesomething[2]{%
  \@namedef{something@#2}{%
    % ...
    \def\something####1{#1}%
    % ...
}}

\show\definesomething
\definesomething{whatever}{Code with #1}%

\show\something@whatever
\something@whatever

\show\something

\stop
|improve this answer|||||
  • Great solution, thanks! (I didn't know about the hash-doubling \unexpanded does.) – user2574 Sep 15 '19 at 16:03
  • Hash-doubling takes place in \edef/\xdef-contexts a) at \the-expansion of <token parameters> and <token registers> b) when the <balanced text> of a <general text> forming the argument of \unexpanded is delivered. This compensates hash-halving at the time of expanding macros. And hash-doubling takes place when writing hashes to external file/screen via (\immediate)\write. It also takes place when \scantokens/\detokenize do their job of unexpanded-writing a pseudo-file and reading that back under current catcode-régime respective \string-catcode-régime. – Ulrich Diez Sep 15 '19 at 16:37
2

Not sure I understand the question, but perhaps:

> \something@whatever=macro:
->\def \something ##1{Code with ##1}.
l.12 \show\something@whatever

? 
> \something=macro:
#1->Code with #1.
l.16 \show\something

? 

from

\makeatletter

\newcommand\definesomething[2]{%
  \@namedef{something@#1}{%
    % ...
    \def\something####1{#2}%
    % ...
  }}

\definesomething{whatever}{Code with ##1}

\show\something@whatever

\something@whatever

\show\something
|improve this answer|||||
  • Was just about to write the same when your answer popped up :) – Skillmon likes topanswers.xyz Sep 13 '19 at 15:04
  • The usage of \definesomething must be \definesomething{whatever}{Code with #1} (and not \definesomething{whatever}{Code with ##1}). I cannot change this for compatibility reasons. – user2574 Sep 13 '19 at 18:29
2

You can double the # tokens by using a token register. However this will require \edef and \unexpanded in the body of the main definition.

\makeatletter
\newcommand\definesomething[2]{%
  \toks@={#2}%
  \expandafter\edef\csname something@#1\endcsname{%
    % ...(all in \unexpanded)
    \def\noexpand\something####1{\the\toks@}%
    % ...(all in \unexpanded)
  }}
\definesomething{whatever}{Code with #1}
\show\something@whatever
\something@whatever
\show\something

The console output is

This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./as.tex
LaTeX2e <2018-12-01>
> \something@whatever=macro:
->\def \something ##1{Code with ##1}.
l.10 \show\something@whatever

? 
> \something=macro:
#1->Code with #1.
l.12 \show\something
|improve this answer|||||

Your Answer

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