How to define a macro with multiple optional parameters?












4















After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:



LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.


...and so on.
The last code I tried looked like this:



%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}


Who can explain what went wrong?



People who like complete examples should add this prolog:



documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}


...and this epilog:



begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}









share|improve this question




















  • 4





    Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.

    – Werner
    Mar 4 at 2:39











  • please extend your code fragment to complete but small document!

    – Zarko
    Mar 4 at 2:40











  • @Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).

    – U. Windl
    Mar 4 at 2:57






  • 2





    it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.

    – David Carlisle
    Mar 4 at 7:53






  • 1





    you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are {}

    – David Carlisle
    Mar 4 at 7:55
















4















After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:



LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.


...and so on.
The last code I tried looked like this:



%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}


Who can explain what went wrong?



People who like complete examples should add this prolog:



documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}


...and this epilog:



begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}









share|improve this question




















  • 4





    Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.

    – Werner
    Mar 4 at 2:39











  • please extend your code fragment to complete but small document!

    – Zarko
    Mar 4 at 2:40











  • @Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).

    – U. Windl
    Mar 4 at 2:57






  • 2





    it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.

    – David Carlisle
    Mar 4 at 7:53






  • 1





    you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are {}

    – David Carlisle
    Mar 4 at 7:55














4












4








4








After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:



LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.


...and so on.
The last code I tried looked like this:



%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}


Who can explain what went wrong?



People who like complete examples should add this prolog:



documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}


...and this epilog:



begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}









share|improve this question
















After I had a macro that worked, I tried to improve it by making some parameters optional.
Unfortunately the macro no longer works. Instead I'm getting errors I do not understand, for example:



LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: fLab undefined.
! Illegal parameter number in definition of fLab.
! Illegal parameter number in definition of reserved@a.
! LaTeX Error: fCap undefined.


...and so on.
The last code I tried looked like this:



%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}


Who can explain what went wrong?



People who like complete examples should add this prolog:



documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}


...and this epilog:



begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}






macros parameters






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 4 at 2:55







U. Windl

















asked Mar 4 at 2:32









U. WindlU. Windl

1256




1256








  • 4





    Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.

    – Werner
    Mar 4 at 2:39











  • please extend your code fragment to complete but small document!

    – Zarko
    Mar 4 at 2:40











  • @Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).

    – U. Windl
    Mar 4 at 2:57






  • 2





    it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.

    – David Carlisle
    Mar 4 at 7:53






  • 1





    you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are {}

    – David Carlisle
    Mar 4 at 7:55














  • 4





    Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.

    – Werner
    Mar 4 at 2:39











  • please extend your code fragment to complete but small document!

    – Zarko
    Mar 4 at 2:40











  • @Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).

    – U. Windl
    Mar 4 at 2:57






  • 2





    it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.

    – David Carlisle
    Mar 4 at 7:53






  • 1





    you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are {}

    – David Carlisle
    Mar 4 at 7:55








4




4





Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.

– Werner
Mar 4 at 2:39





Instead of identifying mistakes in your code without much context, can you provide information on what you want to achieve ultimately? Perhaps there are better ways of achieving it.

– Werner
Mar 4 at 2:39













please extend your code fragment to complete but small document!

– Zarko
Mar 4 at 2:40





please extend your code fragment to complete but small document!

– Zarko
Mar 4 at 2:40













@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).

– U. Windl
Mar 4 at 2:57





@Werner: There are always different ways to reach a goal, but if you change the way too frequently, you'll never make it. So I'd prefer a fix for my "solution" over a completely new attempt (like xparse).

– U. Windl
Mar 4 at 2:57




2




2





it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.

– David Carlisle
Mar 4 at 7:53





it is much easier for people to debug and help you if you provide the code as a complete document that can be run, here you just provide fragments, don't say what error you get and expect people to construct an example and guess that they get the same error that you meant to ask about.

– David Carlisle
Mar 4 at 7:53




1




1





you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are {}

– David Carlisle
Mar 4 at 7:55





you have only defined one optional argument, arguments 4 and 5 are mandatory but you are testing if they are {}

– David Carlisle
Mar 4 at 7:55










3 Answers
3






active

oldest

votes


















1














When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior



documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}

%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{##5}}}%
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}##4}}}}%
begin{figure}[#1]%
centering%
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap%
end{minipage}%
end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}

begin{document}
See ref{foo}.
figCapLab{0.9}{whatever.pdf}{Caption}{foo}
end{document}


, I don't get any of the errors you describe but I get:



LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.


! LaTeX Error: fLab undefined.

See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...

l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?
! Illegal parameter number in definition of fLab.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?

! LaTeX Error: fCap undefined.

See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...

l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?
! Illegal parameter number in definition of fCap.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?

LaTeX Warning: File `whatever.pdf' not found on input line 44.


! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.


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

l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?
! Illegal parameter number in definition of reserved@a.
<to be read again>
4
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?
! Illegal parameter number in definition of reserved@a.
<to be read again>
5
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?
! You can't use `macro parameter character #' in restricted horizontal mode.
<argument> ...e : ignorespaces fLab {small {}##
4}
l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

?
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)

LaTeX Warning: There were undefined references.


When instead I do:



documentclass[a4paper,twoside]{report}
usepackage{german}
usepackage[latin1]{inputenc}
usepackage{a4}
usepackage{amsmath}
usepackage{url}
usepackage{graphicx}
usepackage{ifthen}

% As later renwecommand is done on these macros,
% they should be defined: !!!!!!!!!!!!
newcommand{fLab}{}
newcommand{fCap}{}

%% Graphics figure with caption and label
% #1: optional: placement
% #2: non-optional: relative width
% #3: non-optional: file name
% #4: non-optional: in case not empty: caption
% #5: non-optional: in case not empty: label
newcommand{figCapLab}[5][htbp]{%
ifthenelse{equal{#5}{}}%
{renewcommand{fLab}{}}%
{renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
ifthenelse{equal{#4}{}}%
{renewcommand{fCap}{fLab}}%
{renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
begin{figure}[#1]%
centering
begin{minipage}[t]{#2textwidth}%
includegraphics[width=textwidth]{#3}% is width of surrounding minipage
fCap
end{minipage}%
end{figure}%%%%%%%
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
newcommand{figCap}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{#4}{}}%
{figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
newcommand{figLab}[4]{%
ifthenelse{equal{#1}{}}%
{figCapLab{#2}{#3}{}{#4}}%
{figCapLab[#1]{#2}{#3}{}{#4}}%
}

begin{document}
See ref{foo}.
figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
end{document}


, then I don't get any errors or warnings with the second compilation.





By the way 1:



I don't have whatever.pdf in the texmf-trees of my system.



If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:



E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.



By the way 2:



Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.



By the way 3:



It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.





Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.



There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.



In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.



You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?






share|improve this answer


























  • I named the file whatever.pdf to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.

    – U. Windl
    Mar 5 at 1:05













  • Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.

    – U. Windl
    Mar 5 at 1:22











  • On ##4: I thought if an "inner" newcommand accesses arguments of an outer newcommand, the # has to be doubled regardless whether the inner newcommand has (formal) arguments.

    – U. Windl
    Mar 5 at 2:11











  • @U.Windl The inner newcommand doesn't "access" the argument of the outer newcommand. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand". Within the definition of the outer newcommand the arguments of the inner newcommand are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.

    – Ulrich Diez
    Mar 5 at 9:54



















5














The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.



The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.



Likewise, the 2nd macro must call on the 3rd macro as its final action.



Certain quirky highlights:




  1. if the successive macro is to be called inside of an if block, one must expandafter prior to the successive macro, so that the successive macro doesn't try to absorb the else or fi.


  2. In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.



The MWE:



documentclass{article}
usepackage{graphicx}
newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
{thefigtoks#1}}
newtoksfigtoks
newcommandfigCapLab[3][htbp]{%
figtoks{begin{figure}[#1]}
addtofigtoks{centering}
addtofigtoks{includegraphics[width=#2textwidth]{#3}}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
addtofigtoks{end{figure}}
thefigtoks
else
addtofigtoks{caption{#1}}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
addtofigtoks{end{figure}}
thefigtoks
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

In figures ref{fg:label1} and ref{fg:label2}...
end{document}


enter image description here



Ack-shu-ally, the more I think of it, tokens are not even needed:



documentclass{article}
usepackage{graphicx}
newcommandfigCapLab[3][htbp]{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}
optcap
}
newcommandoptcap[1][relax]{%
ifxrelax#1relax
end{figure}
else
caption{#1}%
expandafterlabelopt
fi
}
newcommandlabelopt[1][relax]{%
ifxrelax#1relaxelselabel{#1}fi
end{figure}
}
begin{document}
figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

In figures ref{fg:label1} and ref{fg:label2}...
end{document}





share|improve this answer


























  • As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)

    – U. Windl
    Mar 5 at 1:17






  • 1





    @U.Windl I will add some context to my answer.

    – Steven B. Segletes
    Mar 5 at 1:22











  • @U.Windl Please see my added commentary.

    – Steven B. Segletes
    Mar 5 at 1:34











  • Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what optcap, relax and expandafter really do. I guess ifx, else, and fi are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.

    – U. Windl
    Mar 5 at 1:51











  • @U.Windl optcap is just the 2nd macro I created in the string of 3 macros. relax is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro causes othermacro to be expanded once, before macro is executed. So, expandafterlabeloptfi causes the fi to be executed (closing out the conditional), so that labelopt will absorb the next token in the input stream, rather than absorbing the fi. Indeed, ifx...else...fi is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion

    – Steven B. Segletes
    Mar 5 at 1:59



















4














Here is how you can achieve your goal using xparse:



enter image description here



documentclass{article}

usepackage{graphicx,xparse}

% figCapLab
% [<float spec>] #1
% {<width factor>} #2
% {<image>} #3
% [<caption>] #4
% [<label>] #5
NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2linewidth]{#3}% Set image at width
IfValueT{#4}
{caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
end{figure}
}

begin{document}

figCapLab{.2}{example-image-a}
figCapLab{.2}{example-image-b}[My caption]
figCapLab{.2}{example-image-c}[My caption][fg:label1]
figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

In figures ref{fg:label1} and ref{fg:label2}ldots

end{document}


Optional arguments with a default is specified using O{<default>} while optional arguments without a default uses o. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}. There are also singular conditionals IfValueT and IfValueF, the former of which was used above.



The above code assumes that an empty caption (blank fourth argument) would not need a label (fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}} out of the <true> branch inside IfValueT{#4}:



NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
begin{figure}[#1]
centering
includegraphics[width=#2textwidth]{#3}% Set image at width
IfValueT{#4}{caption{#4}}% Possible caption
IfValueT{#5}{label{#5}}% Possible label
end{figure}
}





share|improve this answer























    Your Answer








    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "85"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477627%2fhow-to-define-a-macro-with-multiple-optional-parameters%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    %% Graphics figure with caption and label
    % [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{##5}}}%
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}##4}}}}%
    begin{figure}[#1]%
    centering%
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap%
    end{minipage}%
    end{figure}
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{whatever.pdf}{Caption}{foo}
    end{document}


    , I don't get any of the errors you describe but I get:



    LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.


    ! LaTeX Error: fLab undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fLab.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    ! LaTeX Error: fCap undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fCap.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    LaTeX Warning: File `whatever.pdf' not found on input line 44.


    ! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.


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

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! You can't use `macro parameter character #' in restricted horizontal mode.
    <argument> ...e : ignorespaces fLab {small {}##
    4}
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)

    LaTeX Warning: There were undefined references.


    When instead I do:



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    % As later renwecommand is done on these macros,
    % they should be defined: !!!!!!!!!!!!
    newcommand{fLab}{}
    newcommand{fCap}{}

    %% Graphics figure with caption and label
    % #1: optional: placement
    % #2: non-optional: relative width
    % #3: non-optional: file name
    % #4: non-optional: in case not empty: caption
    % #5: non-optional: in case not empty: label
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
    begin{figure}[#1]%
    centering
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap
    end{minipage}%
    end{figure}%%%%%%%
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
    end{document}


    , then I don't get any errors or warnings with the second compilation.





    By the way 1:



    I don't have whatever.pdf in the texmf-trees of my system.



    If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:



    E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.



    By the way 2:



    Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.



    By the way 3:



    It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.





    Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.



    There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.



    In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.



    You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?






    share|improve this answer


























    • I named the file whatever.pdf to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.

      – U. Windl
      Mar 5 at 1:05













    • Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.

      – U. Windl
      Mar 5 at 1:22











    • On ##4: I thought if an "inner" newcommand accesses arguments of an outer newcommand, the # has to be doubled regardless whether the inner newcommand has (formal) arguments.

      – U. Windl
      Mar 5 at 2:11











    • @U.Windl The inner newcommand doesn't "access" the argument of the outer newcommand. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand". Within the definition of the outer newcommand the arguments of the inner newcommand are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.

      – Ulrich Diez
      Mar 5 at 9:54
















    1














    When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    %% Graphics figure with caption and label
    % [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{##5}}}%
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}##4}}}}%
    begin{figure}[#1]%
    centering%
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap%
    end{minipage}%
    end{figure}
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{whatever.pdf}{Caption}{foo}
    end{document}


    , I don't get any of the errors you describe but I get:



    LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.


    ! LaTeX Error: fLab undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fLab.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    ! LaTeX Error: fCap undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fCap.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    LaTeX Warning: File `whatever.pdf' not found on input line 44.


    ! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.


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

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! You can't use `macro parameter character #' in restricted horizontal mode.
    <argument> ...e : ignorespaces fLab {small {}##
    4}
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)

    LaTeX Warning: There were undefined references.


    When instead I do:



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    % As later renwecommand is done on these macros,
    % they should be defined: !!!!!!!!!!!!
    newcommand{fLab}{}
    newcommand{fCap}{}

    %% Graphics figure with caption and label
    % #1: optional: placement
    % #2: non-optional: relative width
    % #3: non-optional: file name
    % #4: non-optional: in case not empty: caption
    % #5: non-optional: in case not empty: label
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
    begin{figure}[#1]%
    centering
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap
    end{minipage}%
    end{figure}%%%%%%%
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
    end{document}


    , then I don't get any errors or warnings with the second compilation.





    By the way 1:



    I don't have whatever.pdf in the texmf-trees of my system.



    If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:



    E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.



    By the way 2:



    Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.



    By the way 3:



    It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.





    Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.



    There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.



    In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.



    You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?






    share|improve this answer


























    • I named the file whatever.pdf to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.

      – U. Windl
      Mar 5 at 1:05













    • Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.

      – U. Windl
      Mar 5 at 1:22











    • On ##4: I thought if an "inner" newcommand accesses arguments of an outer newcommand, the # has to be doubled regardless whether the inner newcommand has (formal) arguments.

      – U. Windl
      Mar 5 at 2:11











    • @U.Windl The inner newcommand doesn't "access" the argument of the outer newcommand. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand". Within the definition of the outer newcommand the arguments of the inner newcommand are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.

      – Ulrich Diez
      Mar 5 at 9:54














    1












    1








    1







    When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    %% Graphics figure with caption and label
    % [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{##5}}}%
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}##4}}}}%
    begin{figure}[#1]%
    centering%
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap%
    end{minipage}%
    end{figure}
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{whatever.pdf}{Caption}{foo}
    end{document}


    , I don't get any of the errors you describe but I get:



    LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.


    ! LaTeX Error: fLab undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fLab.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    ! LaTeX Error: fCap undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fCap.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    LaTeX Warning: File `whatever.pdf' not found on input line 44.


    ! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.


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

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! You can't use `macro parameter character #' in restricted horizontal mode.
    <argument> ...e : ignorespaces fLab {small {}##
    4}
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)

    LaTeX Warning: There were undefined references.


    When instead I do:



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    % As later renwecommand is done on these macros,
    % they should be defined: !!!!!!!!!!!!
    newcommand{fLab}{}
    newcommand{fCap}{}

    %% Graphics figure with caption and label
    % #1: optional: placement
    % #2: non-optional: relative width
    % #3: non-optional: file name
    % #4: non-optional: in case not empty: caption
    % #5: non-optional: in case not empty: label
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
    begin{figure}[#1]%
    centering
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap
    end{minipage}%
    end{figure}%%%%%%%
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
    end{document}


    , then I don't get any errors or warnings with the second compilation.





    By the way 1:



    I don't have whatever.pdf in the texmf-trees of my system.



    If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:



    E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.



    By the way 2:



    Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.



    By the way 3:



    It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.





    Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.



    There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.



    In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.



    You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?






    share|improve this answer















    When I obey your vague instructions for creating a compilable example myself which exhibits the erroneous behavior



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    %% Graphics figure with caption and label
    % [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{##5}}}%
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}##4}}}}%
    begin{figure}[#1]%
    centering%
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap%
    end{minipage}%
    end{figure}
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{whatever.pdf}{Caption}{foo}
    end{document}


    , I don't get any of the errors you describe but I get:



    LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.


    ! LaTeX Error: fLab undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fLab.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    ! LaTeX Error: fCap undefined.

    See the LaTeX manual or LaTeX Companion for explanation.
    Type H <return> for immediate help.
    ...

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of fCap.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?

    LaTeX Warning: File `whatever.pdf' not found on input line 44.


    ! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.


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

    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    4
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! Illegal parameter number in definition of reserved@a.
    <to be read again>
    5
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    ! You can't use `macro parameter character #' in restricted horizontal mode.
    <argument> ...e : ignorespaces fLab {small {}##
    4}
    l.44 figCapLab{0.9}{whatever.pdf}{Caption}{foo}

    ?
    [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)

    LaTeX Warning: There were undefined references.


    When instead I do:



    documentclass[a4paper,twoside]{report}
    usepackage{german}
    usepackage[latin1]{inputenc}
    usepackage{a4}
    usepackage{amsmath}
    usepackage{url}
    usepackage{graphicx}
    usepackage{ifthen}

    % As later renwecommand is done on these macros,
    % they should be defined: !!!!!!!!!!!!
    newcommand{fLab}{}
    newcommand{fCap}{}

    %% Graphics figure with caption and label
    % #1: optional: placement
    % #2: non-optional: relative width
    % #3: non-optional: file name
    % #4: non-optional: in case not empty: caption
    % #5: non-optional: in case not empty: label
    newcommand{figCapLab}[5][htbp]{%
    ifthenelse{equal{#5}{}}%
    {renewcommand{fLab}{}}%
    {renewcommand{fLab}{label{#5}}}% !!!!Don't double the hash!!!!
    ifthenelse{equal{#4}{}}%
    {renewcommand{fCap}{fLab}}%
    {renewcommand{fCap}{caption{fLab{small{}#4}}}}% !!!!Don't double the hash!!!!
    begin{figure}[#1]%
    centering
    begin{minipage}[t]{#2textwidth}%
    includegraphics[width=textwidth]{#3}% is width of surrounding minipage
    fCap
    end{minipage}%
    end{figure}%%%%%%%
    }
    %% Graphics figure with caption
    % [1:placement,] 2:relative width, 3:file name, 4:caption
    newcommand{figCap}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{#4}{}}%
    {figCapLab[#1]{#2}{#3}{#4}{}}%
    }
    %% Graphics figure with label
    % [1:placement,] 2:relative width, 3:file name, 4:label
    newcommand{figLab}[4]{%
    ifthenelse{equal{#1}{}}%
    {figCapLab{#2}{#3}{}{#4}}%
    {figCapLab[#1]{#2}{#3}{}{#4}}%
    }

    begin{document}
    See ref{foo}.
    figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
    end{document}


    , then I don't get any errors or warnings with the second compilation.





    By the way 1:



    I don't have whatever.pdf in the texmf-trees of my system.



    If you wish to provide examples that process images, you can use the ready-to-use images that modern LaTeX systems offer to you:



    E.g., the documentation of Martin Scharrer's MWE package lists all the images available due to that package. With current TeX-platforms and current releases of the MWE-package, the images are usable without the need of loading the package because they are integrated into the texmf-tree and into the filename-database.



    By the way 2:



    Under normal circumstances you may indent lines of TeX source code for improving readability. ;-) This is because when (La)TeX begins to read a line of input, the state of the reading-apparatus is in state N (new line) while in state N characters of category code 10(space) will not be tokenized as space tokens but will not yield any tokens at all.



    By the way 3:



    It is good practice to provide examples which people can compile as they are for exactly reproducing the erroneous behavior. Being able to reproduce erroneous behavior is important for being able to debug the code which produces that erroneous behavior.





    Dr. Nicola Talbot's Creating a LaTeX Minimal Example provides some guidelines.



    There also is How to make a “minimum example” from texfaq.org which also contains some links to advices for asking questions.



    In What is a minimal working example? Christian Faulhammer does not use the term "Minimal Example" but he uses the term "Minimal Working Example" even for examples which work only in the sense that they are sufficient for exhibiting an erroneous behavior.



    You may also be interested in the answers to the question I've just been asked to write a minimal example, what is that?







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Mar 5 at 9:55

























    answered Mar 4 at 9:02









    Ulrich DiezUlrich Diez

    5,305619




    5,305619













    • I named the file whatever.pdf to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.

      – U. Windl
      Mar 5 at 1:05













    • Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.

      – U. Windl
      Mar 5 at 1:22











    • On ##4: I thought if an "inner" newcommand accesses arguments of an outer newcommand, the # has to be doubled regardless whether the inner newcommand has (formal) arguments.

      – U. Windl
      Mar 5 at 2:11











    • @U.Windl The inner newcommand doesn't "access" the argument of the outer newcommand. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand". Within the definition of the outer newcommand the arguments of the inner newcommand are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.

      – Ulrich Diez
      Mar 5 at 9:54



















    • I named the file whatever.pdf to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.

      – U. Windl
      Mar 5 at 1:05













    • Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.

      – U. Windl
      Mar 5 at 1:22











    • On ##4: I thought if an "inner" newcommand accesses arguments of an outer newcommand, the # has to be doubled regardless whether the inner newcommand has (formal) arguments.

      – U. Windl
      Mar 5 at 2:11











    • @U.Windl The inner newcommand doesn't "access" the argument of the outer newcommand. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand". Within the definition of the outer newcommand the arguments of the inner newcommand are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.

      – Ulrich Diez
      Mar 5 at 9:54

















    I named the file whatever.pdf to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.

    – U. Windl
    Mar 5 at 1:05







    I named the file whatever.pdf to emphasize the fact that the actual file name is unimportant for the problem, and you can use any PDF file you like (assuming everyone who is going to answer this problem either has some PDF file or is able to create one). As my text installation (openSUSE) consists of more than 1000 individual packages, I must admit that I lost overview of what is needed for what. On lack of spaces: I'm still unsure which spaces will be ignored and which aren't , so I used no indent.

    – U. Windl
    Mar 5 at 1:05















    Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.

    – U. Windl
    Mar 5 at 1:22





    Would it be possible to provide a context diff of the changes required to my definitions? I feel you thought "you made it hard for me to understand, so I'll make it hard for you to understand". And as written for another answer: Maybe explain what I did wrong.

    – U. Windl
    Mar 5 at 1:22













    On ##4: I thought if an "inner" newcommand accesses arguments of an outer newcommand, the # has to be doubled regardless whether the inner newcommand has (formal) arguments.

    – U. Windl
    Mar 5 at 2:11





    On ##4: I thought if an "inner" newcommand accesses arguments of an outer newcommand, the # has to be doubled regardless whether the inner newcommand has (formal) arguments.

    – U. Windl
    Mar 5 at 2:11













    @U.Windl The inner newcommand doesn't "access" the argument of the outer newcommand. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand". Within the definition of the outer newcommand the arguments of the inner newcommand are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.

    – Ulrich Diez
    Mar 5 at 9:54





    @U.Windl The inner newcommand doesn't "access" the argument of the outer newcommand. At the time of carrying out the "outer command", which was defined at an earlier point in time by what you call "the outer newcommand", TeX uses the argument of the "outer command" for defining the "inner command" by carrying out the "inner newcommand". Within the definition of the outer newcommand the arguments of the inner newcommand are to be denoted by means of doubled hashes because when carrying out the outer command that defines the inner command, two hashes are collapsed into one.

    – Ulrich Diez
    Mar 5 at 9:54











    5














    The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.



    The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.



    Likewise, the 2nd macro must call on the 3rd macro as its final action.



    Certain quirky highlights:




    1. if the successive macro is to be called inside of an if block, one must expandafter prior to the successive macro, so that the successive macro doesn't try to absorb the else or fi.


    2. In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.



    The MWE:



    documentclass{article}
    usepackage{graphicx}
    newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
    {thefigtoks#1}}
    newtoksfigtoks
    newcommandfigCapLab[3][htbp]{%
    figtoks{begin{figure}[#1]}
    addtofigtoks{centering}
    addtofigtoks{includegraphics[width=#2textwidth]{#3}}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    addtofigtoks{end{figure}}
    thefigtoks
    else
    addtofigtoks{caption{#1}}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
    addtofigtoks{end{figure}}
    thefigtoks
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}


    enter image description here



    Ack-shu-ally, the more I think of it, tokens are not even needed:



    documentclass{article}
    usepackage{graphicx}
    newcommandfigCapLab[3][htbp]{%
    begin{figure}[#1]
    centering
    includegraphics[width=#2textwidth]{#3}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    end{figure}
    else
    caption{#1}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelselabel{#1}fi
    end{figure}
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}





    share|improve this answer


























    • As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)

      – U. Windl
      Mar 5 at 1:17






    • 1





      @U.Windl I will add some context to my answer.

      – Steven B. Segletes
      Mar 5 at 1:22











    • @U.Windl Please see my added commentary.

      – Steven B. Segletes
      Mar 5 at 1:34











    • Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what optcap, relax and expandafter really do. I guess ifx, else, and fi are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.

      – U. Windl
      Mar 5 at 1:51











    • @U.Windl optcap is just the 2nd macro I created in the string of 3 macros. relax is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro causes othermacro to be expanded once, before macro is executed. So, expandafterlabeloptfi causes the fi to be executed (closing out the conditional), so that labelopt will absorb the next token in the input stream, rather than absorbing the fi. Indeed, ifx...else...fi is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion

      – Steven B. Segletes
      Mar 5 at 1:59
















    5














    The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.



    The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.



    Likewise, the 2nd macro must call on the 3rd macro as its final action.



    Certain quirky highlights:




    1. if the successive macro is to be called inside of an if block, one must expandafter prior to the successive macro, so that the successive macro doesn't try to absorb the else or fi.


    2. In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.



    The MWE:



    documentclass{article}
    usepackage{graphicx}
    newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
    {thefigtoks#1}}
    newtoksfigtoks
    newcommandfigCapLab[3][htbp]{%
    figtoks{begin{figure}[#1]}
    addtofigtoks{centering}
    addtofigtoks{includegraphics[width=#2textwidth]{#3}}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    addtofigtoks{end{figure}}
    thefigtoks
    else
    addtofigtoks{caption{#1}}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
    addtofigtoks{end{figure}}
    thefigtoks
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}


    enter image description here



    Ack-shu-ally, the more I think of it, tokens are not even needed:



    documentclass{article}
    usepackage{graphicx}
    newcommandfigCapLab[3][htbp]{%
    begin{figure}[#1]
    centering
    includegraphics[width=#2textwidth]{#3}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    end{figure}
    else
    caption{#1}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelselabel{#1}fi
    end{figure}
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}





    share|improve this answer


























    • As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)

      – U. Windl
      Mar 5 at 1:17






    • 1





      @U.Windl I will add some context to my answer.

      – Steven B. Segletes
      Mar 5 at 1:22











    • @U.Windl Please see my added commentary.

      – Steven B. Segletes
      Mar 5 at 1:34











    • Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what optcap, relax and expandafter really do. I guess ifx, else, and fi are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.

      – U. Windl
      Mar 5 at 1:51











    • @U.Windl optcap is just the 2nd macro I created in the string of 3 macros. relax is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro causes othermacro to be expanded once, before macro is executed. So, expandafterlabeloptfi causes the fi to be executed (closing out the conditional), so that labelopt will absorb the next token in the input stream, rather than absorbing the fi. Indeed, ifx...else...fi is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion

      – Steven B. Segletes
      Mar 5 at 1:59














    5












    5








    5







    The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.



    The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.



    Likewise, the 2nd macro must call on the 3rd macro as its final action.



    Certain quirky highlights:




    1. if the successive macro is to be called inside of an if block, one must expandafter prior to the successive macro, so that the successive macro doesn't try to absorb the else or fi.


    2. In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.



    The MWE:



    documentclass{article}
    usepackage{graphicx}
    newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
    {thefigtoks#1}}
    newtoksfigtoks
    newcommandfigCapLab[3][htbp]{%
    figtoks{begin{figure}[#1]}
    addtofigtoks{centering}
    addtofigtoks{includegraphics[width=#2textwidth]{#3}}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    addtofigtoks{end{figure}}
    thefigtoks
    else
    addtofigtoks{caption{#1}}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
    addtofigtoks{end{figure}}
    thefigtoks
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}


    enter image description here



    Ack-shu-ally, the more I think of it, tokens are not even needed:



    documentclass{article}
    usepackage{graphicx}
    newcommandfigCapLab[3][htbp]{%
    begin{figure}[#1]
    centering
    includegraphics[width=#2textwidth]{#3}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    end{figure}
    else
    caption{#1}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelselabel{#1}fi
    end{figure}
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}





    share|improve this answer















    The OP asked me for a little explanation to this answer so I will add some text here. The standard LaTeX mechanism for optional arguments allows for one optional argument that comes prior to mandatory arguments. Thus, to create a syntax that provides an optional argument, 2 mandatory arguments, and then 2 optional arguments, requires 3 macros to be strung together successively, since there are 3 optional arguments requested.



    The first macro absorbs an optional plus 2 mandatory arguments, and then it must invoke a second macro, and here is the key, as its final action! The reason the 2nd macro invocation must be the final action of the first macro is that the 2nd macro must absorb an optional argument. If there are any tokens in the first macro that follow the invocation of the 2nd macro in the chain, then those other tokens in the 1st macro will be absorbed as the argument, instead of what is intended.



    Likewise, the 2nd macro must call on the 3rd macro as its final action.



    Certain quirky highlights:




    1. if the successive macro is to be called inside of an if block, one must expandafter prior to the successive macro, so that the successive macro doesn't try to absorb the else or fi.


    2. In this particular case, the final optional argument, the label, is only callable if a caption has been specified. Thus, the 2nd macro in the chain will only call on the 3rd macro if a caption has been specified. Otherwise, it will truncate the sequence.



    The MWE:



    documentclass{article}
    usepackage{graphicx}
    newcommandaddtofigtoks[1]{expandafterfigtoksexpandafter
    {thefigtoks#1}}
    newtoksfigtoks
    newcommandfigCapLab[3][htbp]{%
    figtoks{begin{figure}[#1]}
    addtofigtoks{centering}
    addtofigtoks{includegraphics[width=#2textwidth]{#3}}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    addtofigtoks{end{figure}}
    thefigtoks
    else
    addtofigtoks{caption{#1}}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelseaddtofigtoks{label{#1}}fi
    addtofigtoks{end{figure}}
    thefigtoks
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}


    enter image description here



    Ack-shu-ally, the more I think of it, tokens are not even needed:



    documentclass{article}
    usepackage{graphicx}
    newcommandfigCapLab[3][htbp]{%
    begin{figure}[#1]
    centering
    includegraphics[width=#2textwidth]{#3}
    optcap
    }
    newcommandoptcap[1][relax]{%
    ifxrelax#1relax
    end{figure}
    else
    caption{#1}%
    expandafterlabelopt
    fi
    }
    newcommandlabelopt[1][relax]{%
    ifxrelax#1relaxelselabel{#1}fi
    end{figure}
    }
    begin{document}
    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}...
    end{document}






    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Mar 5 at 1:33

























    answered Mar 4 at 3:33









    Steven B. SegletesSteven B. Segletes

    158k9204411




    158k9204411













    • As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)

      – U. Windl
      Mar 5 at 1:17






    • 1





      @U.Windl I will add some context to my answer.

      – Steven B. Segletes
      Mar 5 at 1:22











    • @U.Windl Please see my added commentary.

      – Steven B. Segletes
      Mar 5 at 1:34











    • Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what optcap, relax and expandafter really do. I guess ifx, else, and fi are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.

      – U. Windl
      Mar 5 at 1:51











    • @U.Windl optcap is just the 2nd macro I created in the string of 3 macros. relax is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro causes othermacro to be expanded once, before macro is executed. So, expandafterlabeloptfi causes the fi to be executed (closing out the conditional), so that labelopt will absorb the next token in the input stream, rather than absorbing the fi. Indeed, ifx...else...fi is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion

      – Steven B. Segletes
      Mar 5 at 1:59



















    • As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)

      – U. Windl
      Mar 5 at 1:17






    • 1





      @U.Windl I will add some context to my answer.

      – Steven B. Segletes
      Mar 5 at 1:22











    • @U.Windl Please see my added commentary.

      – Steven B. Segletes
      Mar 5 at 1:34











    • Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what optcap, relax and expandafter really do. I guess ifx, else, and fi are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.

      – U. Windl
      Mar 5 at 1:51











    • @U.Windl optcap is just the 2nd macro I created in the string of 3 macros. relax is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro causes othermacro to be expanded once, before macro is executed. So, expandafterlabeloptfi causes the fi to be executed (closing out the conditional), so that labelopt will absorb the next token in the input stream, rather than absorbing the fi. Indeed, ifx...else...fi is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion

      – Steven B. Segletes
      Mar 5 at 1:59

















    As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)

    – U. Windl
    Mar 5 at 1:17





    As I tried to point out: Unlike most people asking questions, I don't just want a solution to copy without understanding, but instead I want to learn what I did wrong to use that knowledge for the future. Sorry, I'm pre-Google generation ;-)

    – U. Windl
    Mar 5 at 1:17




    1




    1





    @U.Windl I will add some context to my answer.

    – Steven B. Segletes
    Mar 5 at 1:22





    @U.Windl I will add some context to my answer.

    – Steven B. Segletes
    Mar 5 at 1:22













    @U.Windl Please see my added commentary.

    – Steven B. Segletes
    Mar 5 at 1:34





    @U.Windl Please see my added commentary.

    – Steven B. Segletes
    Mar 5 at 1:34













    Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what optcap, relax and expandafter really do. I guess ifx, else, and fi are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.

    – U. Windl
    Mar 5 at 1:51





    Imagine (the truth!) that the user asking the question only knows LaTeX, and not TeX, so the user wonders what optcap, relax and expandafter really do. I guess ifx, else, and fi are TeX's low-level conditionals. When describing the processing in an abstract way (as you do), some concrete examples (e.g. actual arguments) would be helpful for the TeX Dummies like me.

    – U. Windl
    Mar 5 at 1:51













    @U.Windl optcap is just the 2nd macro I created in the string of 3 macros. relax is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro causes othermacro to be expanded once, before macro is executed. So, expandafterlabeloptfi causes the fi to be executed (closing out the conditional), so that labelopt will absorb the next token in the input stream, rather than absorbing the fi. Indeed, ifx...else...fi is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion

    – Steven B. Segletes
    Mar 5 at 1:59





    @U.Windl optcap is just the 2nd macro I created in the string of 3 macros. relax is a macro that does nothing---however, it can be tested for, which is how I use it here. The sequence expandaftermacroothermacro causes othermacro to be expanded once, before macro is executed. So, expandafterlabeloptfi causes the fi to be executed (closing out the conditional), so that labelopt will absorb the next token in the input stream, rather than absorbing the fi. Indeed, ifx...else...fi is one type of TeX conditional that compares the next 2 tokens WITHOUT expansion

    – Steven B. Segletes
    Mar 5 at 1:59











    4














    Here is how you can achieve your goal using xparse:



    enter image description here



    documentclass{article}

    usepackage{graphicx,xparse}

    % figCapLab
    % [<float spec>] #1
    % {<width factor>} #2
    % {<image>} #3
    % [<caption>] #4
    % [<label>] #5
    NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
    begin{figure}[#1]
    centering
    includegraphics[width=#2linewidth]{#3}% Set image at width
    IfValueT{#4}
    {caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
    end{figure}
    }

    begin{document}

    figCapLab{.2}{example-image-a}
    figCapLab{.2}{example-image-b}[My caption]
    figCapLab{.2}{example-image-c}[My caption][fg:label1]
    figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

    In figures ref{fg:label1} and ref{fg:label2}ldots

    end{document}


    Optional arguments with a default is specified using O{<default>} while optional arguments without a default uses o. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}. There are also singular conditionals IfValueT and IfValueF, the former of which was used above.



    The above code assumes that an empty caption (blank fourth argument) would not need a label (fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}} out of the <true> branch inside IfValueT{#4}:



    NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
    begin{figure}[#1]
    centering
    includegraphics[width=#2textwidth]{#3}% Set image at width
    IfValueT{#4}{caption{#4}}% Possible caption
    IfValueT{#5}{label{#5}}% Possible label
    end{figure}
    }





    share|improve this answer




























      4














      Here is how you can achieve your goal using xparse:



      enter image description here



      documentclass{article}

      usepackage{graphicx,xparse}

      % figCapLab
      % [<float spec>] #1
      % {<width factor>} #2
      % {<image>} #3
      % [<caption>] #4
      % [<label>] #5
      NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
      begin{figure}[#1]
      centering
      includegraphics[width=#2linewidth]{#3}% Set image at width
      IfValueT{#4}
      {caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
      end{figure}
      }

      begin{document}

      figCapLab{.2}{example-image-a}
      figCapLab{.2}{example-image-b}[My caption]
      figCapLab{.2}{example-image-c}[My caption][fg:label1]
      figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

      In figures ref{fg:label1} and ref{fg:label2}ldots

      end{document}


      Optional arguments with a default is specified using O{<default>} while optional arguments without a default uses o. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}. There are also singular conditionals IfValueT and IfValueF, the former of which was used above.



      The above code assumes that an empty caption (blank fourth argument) would not need a label (fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}} out of the <true> branch inside IfValueT{#4}:



      NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
      begin{figure}[#1]
      centering
      includegraphics[width=#2textwidth]{#3}% Set image at width
      IfValueT{#4}{caption{#4}}% Possible caption
      IfValueT{#5}{label{#5}}% Possible label
      end{figure}
      }





      share|improve this answer


























        4












        4








        4







        Here is how you can achieve your goal using xparse:



        enter image description here



        documentclass{article}

        usepackage{graphicx,xparse}

        % figCapLab
        % [<float spec>] #1
        % {<width factor>} #2
        % {<image>} #3
        % [<caption>] #4
        % [<label>] #5
        NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
        begin{figure}[#1]
        centering
        includegraphics[width=#2linewidth]{#3}% Set image at width
        IfValueT{#4}
        {caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
        end{figure}
        }

        begin{document}

        figCapLab{.2}{example-image-a}
        figCapLab{.2}{example-image-b}[My caption]
        figCapLab{.2}{example-image-c}[My caption][fg:label1]
        figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

        In figures ref{fg:label1} and ref{fg:label2}ldots

        end{document}


        Optional arguments with a default is specified using O{<default>} while optional arguments without a default uses o. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}. There are also singular conditionals IfValueT and IfValueF, the former of which was used above.



        The above code assumes that an empty caption (blank fourth argument) would not need a label (fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}} out of the <true> branch inside IfValueT{#4}:



        NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
        begin{figure}[#1]
        centering
        includegraphics[width=#2textwidth]{#3}% Set image at width
        IfValueT{#4}{caption{#4}}% Possible caption
        IfValueT{#5}{label{#5}}% Possible label
        end{figure}
        }





        share|improve this answer













        Here is how you can achieve your goal using xparse:



        enter image description here



        documentclass{article}

        usepackage{graphicx,xparse}

        % figCapLab
        % [<float spec>] #1
        % {<width factor>} #2
        % {<image>} #3
        % [<caption>] #4
        % [<label>] #5
        NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
        begin{figure}[#1]
        centering
        includegraphics[width=#2linewidth]{#3}% Set image at width
        IfValueT{#4}
        {caption{#4}IfValueT{#5}{label{#5}}}% Set possible caption and label
        end{figure}
        }

        begin{document}

        figCapLab{.2}{example-image-a}
        figCapLab{.2}{example-image-b}[My caption]
        figCapLab{.2}{example-image-c}[My caption][fg:label1]
        figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

        In figures ref{fg:label1} and ref{fg:label2}ldots

        end{document}


        Optional arguments with a default is specified using O{<default>} while optional arguments without a default uses o. Conditioning on whether or not a value is supplied is done using IfValueTF{<parameter>}{<true>}{<false>}. There are also singular conditionals IfValueT and IfValueF, the former of which was used above.



        The above code assumes that an empty caption (blank fourth argument) would not need a label (fifth) argument. If that's needed, move the IfValueT{#5}{label{#5}} out of the <true> branch inside IfValueT{#4}:



        NewDocumentCommand{figCapLab}{ O{htbp} m m o o }{%
        begin{figure}[#1]
        centering
        includegraphics[width=#2textwidth]{#3}% Set image at width
        IfValueT{#4}{caption{#4}}% Possible caption
        IfValueT{#5}{label{#5}}% Possible label
        end{figure}
        }






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Mar 4 at 5:52









        WernerWerner

        447k699891695




        447k699891695






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to TeX - LaTeX Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f477627%2fhow-to-define-a-macro-with-multiple-optional-parameters%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

            ComboBox Display Member on multiple fields

            Is it possible to collect Nectar points via Trainline?