# Expandable string substitution

I am trying to define a macro

\formatstr{\mystring}{(string empty)}{the string is: #1}


which expands to (string empty) when \mystring is empty (as judged, e.g. by etoolbox's \csdefempty) and expands to the string is: \mystring otherwise.

I could write, for example,

\newcommand*\formatstr[3]{%
\def\fmt##1{#3}%
\ifdefempty#1{#2}{\fmt{#1}}%
}


which works as I want, except for the fact that \formatstr itself is not expandable because of the \def assignment.

Is there any other way in which I could substitute the #1 in the third argument of \formatstr with \mystring in an expandable way?

Edit: Let me try to more clearly explain what I'm trying to do

Using etoolbox I can type things such as

\ifdefempty{\mystring}{(empty string)}{the string \mystring\ is funny}


But this gets tedious if I have to write

\ifdefempty{\mystringwithaverylongname}{(empty string)}{the string \mystringwithaverylongname\ is long}


So what I would like to be able to write is

\formatstr{\mystringwithaverylongname}{(empty string)}{some string #1 is nice}


Or perhaps

\formatstr{\mystringwithaverylongname}{(empty string)}{i've heard that :str is interesting}


What I want to know if is it possible to substitute the #1 (or :str) in the third argument with the contents coming from the first?

• I have a feeling this has come up before probably with an egreg answer. – Joseph Wright Jul 25 '12 at 16:07
• I'm confused. Are you just after \ifx#1\@empty? – Joseph Wright Jul 25 '12 at 16:16
• No, the emptiness check is fine, the problem is the \def! – Juan A. Navarro Jul 25 '12 at 16:19
• I don't see why you need the \def at all. If you want to use \ifdefempty, why not just \ifdefempty{#1}...? – Joseph Wright Jul 25 '12 at 16:24
• Because I want to replace the string is: #1 with the string is: \mystring. – Juan A. Navarro Jul 25 '12 at 16:25

You need a wrapper around \ifdefempty functioning as an argument processor. For instance:

\def\myifdefempty#1#2#3:str#4|{%
\ifdefempty{#1}{#2}{#3#1#4}%
}


Full minimal example:

\documentclass{article}

\usepackage{etoolbox}

\usepackage{hyperref}

\newcommand*{\formatstr}[3]{%
\myifdefempty{#1}{#2}#3|%
}

\def\myifdefempty#1#2#3:str#4|{%
\ifdefempty{#1}{#2}{#3#1#4}%
}

\begin{document}

\tableofcontents

\def\mystring{}

\section{\formatstr{\mystring}{an empty string}{the string :str is funny}}

\def\mystring{a'}

\section{\formatstr{\mystring}{an empty string}{the string :str is funny}}

\end{document}

• this is perhaps closer to what was asked for but probably worth noting that :str needs to be at the top level not in {} groups. (Of course a similar restriction is enforced in my version by splitting the argument:-) – David Carlisle Jul 25 '12 at 19:04
• @DavidCarlisle: Some day, I’ll understand TeX a bit :-) – mhp Jul 25 '12 at 19:27
• Very nice and very creative! I was trying to fish for something more general, but I'm quite happy with this solution. Thanks! – Juan A. Navarro Jul 25 '12 at 20:34

The usual idiom is something like

\makeatletter

\begingroup
\lccode\!=0
\lowercase{\endgroup
\newcommand*\formatstr[3]{%
\if!#1!%
\expandafter\@firstofthree
\else
\expandafter\@secondoftwo
\fi{#2}{#3}{#1}}}

\long\def\@firstofthree#1#2#3{#1}

\makeatother

\def\foo{}

\typeout{A: \formatstr\foo{string is emty}{the string is: \fmt}}

\def\fmt#1{[[#1]]}
\def\foo{hello}

\typeout{B: \formatstr\foo{string is emty}{the string is: \fmt}}

\stop


which produces the output (in \typeout to show it is expandable

A: string is emty
B: the string is: [[hello]]


Or this version which does not require a \fmt helper macro.

Here, as noted in comments, the syntax is

  \fmtstring\token{empty}{non-empth-pre}{non-empty-post}


with empty being used in the empty case and the expansion of

non-empth-pre\token non-empty-post


being used in the non-empty case.

A: string is empty
B: the string is: [hello]


from the slightly simpler code:

\makeatletter

\begingroup
\lccode\!=0
\lowercase{\endgroup
\newcommand*\formatstr[4]{%
\if!#1!%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi{#2}{#3#1#4}}}

\makeatother

\def\foo{}
\typeout{A: \formatstr\foo{string is empty}{the string is: [}{]}}

\def\foo{hello}
\typeout{B: \formatstr\foo{string is empty}{the string is: [}{]}}

\stop

• But if I want to change the output of the non-empty case, then I have to redefine \fmt before using \formatstr. But that's the information that I wanted to pass as the third argument of \formatstr! – Juan A. Navarro Jul 25 '12 at 16:24
• well... if you just want it to come at the end the you don't need fmt at all. If you want to be able to make it appear in the middle of some arbitrary string then you need to make a non-expandable definition somewhere. Would it be ok to have tree arguments \formatstr\token{empty}{not-empty-pre}{not-empty-post} so you could go \formatstr\foo{string is emty}{the string is:[[}{]]} That wouldn't require \fmt – David Carlisle Jul 25 '12 at 16:32
• Yes, I want it to show possibly in the middle of somethings. I've considered, as you mention, passing four arguments rather than three, but I thought the other syntax is nicer/more concise. My question was whether that was possible at all. In fact, I don't really care if I have to use #1 or :str on the non-empty case; what I would like is to be able to substitute such marker with the \mystring coming as the first argument, so that I don't have to type the contents of the first argument twice. – Juan A. Navarro Jul 25 '12 at 16:38
• since the first argument os always a single token, it would be a lot easier with a lot less restrictions just to use it in the third argument \formatstr{\mystring}{(string empty)}{the string is: \mystring} seems as easy to enter as the version in the question? – David Carlisle Jul 25 '12 at 19:41

I guess this is what Joseph Wright is saying, which sounds logical to me, but I don't think I understand the OP well. \ifdefempty is expandable in a rather expensive way.

\documentclass{article}
\usepackage{etoolbox}
\newcommand*\formatstr[3]{\ifdefempty{#1}{#2}{#3#1}}
\newcommand*\bformatstr[3]{\ifblank{#1}{#2}{#3{#1}}}
\begin{document}
\def\fmt#1{[[#1]]}
\def\foo{}
\def\vgap{\par\bigskip}

\textbf{Case A1}: \formatstr\foo{string is empty}{the string is: \fmt}
\vgap
\textbf{Case A2}: \bformatstr{}{string is empty}{the string is: \fmt}
\vgap
\def\foo{hello}
\textbf{Case B1}: \formatstr\foo{string is empty}{the string is: \fmt}
\vgap
\textbf{Case B2}: \bformatstr{hello}{string is empty}{the string is: \fmt}
\end{document}


• \ifdefempty` ignores prefixes. Moreover, control sequences whose replacement text is blank space are considered empty, too. The former is a documented feature, the latter is undocumented. – mhp Jul 26 '12 at 6:13