I favor the answer of egreg, i.e., the suggestion of using \@dblarg
.
Werner suggested checking for a "default value" in terms of an \ifx
-comparison.
Checking for a default value is somewhat different from checking whether an optional argument was provided at all as the case of the optional argument not being provided at all is not distinguished from the case of the optional argument being provided explicitly with the default value.
\ifx
-comparison implies that
- either comparison is to be done in a non expandable way by defining and
\ifx
-comparing temporary macros
- or
\ifx
-comparison is to be applied directly to the token forming the default value and the token(s) actually supplied as optional argument.
In the latter case \ifx
-comparison imposes some restrictions on what can be used as default value and is not completely "waterproof":
- Default values cannot consist of several tokens.
- As
\ifx
-comparison does compare single tokens, not macro arguments, \ifx
-comparison can be outmanoeuvred in several erroneous ways in edge cases where the arguments consist of more than one token.
- An
\ifx
-comparison can also be outmanoeuvred by the edge case of arguments containing unbalanced \else
or \fi
.
\ifx
-comparisons can be outmanoeuvred by using control sequence tokens or active character tokens \let
equal to the default-value-token.
If going the route of checking for a default value in an expandable way, i.e., not defining some temporary macros and \ifx
-comparing them, I suggest
either having "emptiness" as default value and checking for emptiness:
\documentclass{article}
\makeatletter
%%=========================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo
%%.........................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
%%-------------------------------------------------------------------------
%% Check whether argument is empty:
%%.........................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% A concern in his posting is that the argument is hit with \string
%% after some expansions which in edge cases might result in unbalancing
%% surrounding \if..\fi-constructs if the macro is used inside of such
%% \if..\fi-constructs.
%%
%% That challenging concern sickened me. ;-)
%%
%% Therefore I decided to implerment a variant where this cannot happen
%% as expansion is forced by \romannumeral:
%%
%% After the first expansion-step, \string is not applied yet.
%% After the second expansion-step, any possibly disturbing remainders
%% are already removed due to \romannumeral-expansion.
%%
%% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
%% digit 0, space token for terminating \romannumeral-expansion,
%% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
%%
%% May 20, 2016
%%
%% Ulrich Diez (e-mail: ud.usenetcorrespondence@web.de)
%%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
\newcommand{\foo}[2][]{%
\UD@CheckWhetherNull{#1}{#2}{#1}~foo(#2)%
}%
\makeatother
\parindent=0ex
\parskip=\bigskipamount
\begin{document}
Empty optional argument or no optional argument will be given---%
\verb|\foo{hi}|:\\
\foo{hi}
Empty optional argument or no optional argument will be given---%
\verb|\foo[]{hi}|:\\
\foo[]{hi}
Empty optional argument or no optional argument will be given---%
\verb|\foo[{}]{hi}|:\\
\foo[{}]{hi}
A nice optional argument will be given---%
\verb|\foo[hello]{hi}|:\\
\foo[hello]{hi}
\end{document}

or checking for the default value by means of macros that process delimited arguments:
\documentclass{article}
\makeatletter
%%=========================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo
%%.........................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
%%-------------------------------------------------------------------------
%% Check whether argument is empty:
%%.........................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% A concern in his posting is that the argument is hit with \string
%% after some expansions which in edge cases might result in unbalancing
%% surrounding \if..\fi-constructs if the macro is used inside of such
%% \if..\fi-constructs.
%%
%% That challenging concern sickened me. ;-)
%%
%% Therefore I decided to implerment a variant where this cannot happen
%% as expansion is forced by \romannumeral:
%%
%% After the first expansion-step, \string is not applied yet.
%% After the second expansion-step, any possibly disturbing remainders
%% are already removed due to \romannumeral-expansion.
%%
%% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
%% digit 0, space token for terminating \romannumeral-expansion,
%% \string, \expandafter, \UD@firstoftwo, \UD@secondoftwo, {, }.
%%
%% May 20, 2016
%%
%% Ulrich Diez (e-mail: ud.usenetcorrespondence@web.de)
%%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-------------------------------------------------------------------------
%% Check whether argument contains no exclamation-mark on top-brace-level:
%%.........................................................................
%% \UD@CheckWhetherNoExclamationMark{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% argument which is to be checked does not contain !>}%
%% {<Tokens to be delivered in case that
%% argument which is to be checked does contain !>}%
\long\def\UD@RemoveToExclamationMark#1!{}%
\long\def\UD@CheckWhetherNoExclamationMark#1{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@RemoveToExclamationMark#1!}%
}%
%%-------------------------------------------------------------------------
%% Fork depending on some tokens:
%%.........................................................................
%%\\CheckWhetherDefault{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case argument is "Default value">}%
%% {<Tokens to be delivered in case argument is not "Default value">}%
%%
%% In case <Argument which is to be checked> is neither "case 1" nor
%% "case 2" the phrase "Error: Unknown parameter ``<Argument which is
%% to be checked>'' to \CheckWhetherDefault." will be delivered.
%%
\newcommand\@CheckWhetherDefault{}%
\long\def\@CheckWhetherDefault%
#1!!Default value!#2#3!!!!{#2}%
\newcommand\CheckWhetherDefault[1]{%
\romannumeral0%
\UD@CheckWhetherNoExclamationMark{#1}{%
\@CheckWhetherDefault
!#1!Default value!{\UD@firstoftwo}% <- #1 is empty.
!!#1!{\UD@firstoftwo}% <- #1 = Default value
!!Default value!{\UD@secondoftwo}% <- #1 = something else without exclamation mark
!!!!%
}{\UD@secondoftwo}% <- #1 = something else with exclamation mark
}%
\makeatother
\newcommand{\foo}[2][Default value]{%
\CheckWhetherDefault{#1}{#2}%
{#1}%
~foo(#2)%
}%
\parindent=0ex
\parskip=\bigskipamount
\begin{document}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo{hi}|:\\
\foo{hi}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo[Default value]{hi}|:\\
\foo[Default value]{hi}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo[]{hi}|:\\
\foo[]{hi}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo[{}]{hi}|:\\
\foo[{}]{hi}
A nice optional argument will be given---%
\verb|\foo[hello]{hi}|:\\
\foo[hello]{hi}
\end{document}

You can also go the boring non expandable route of defining and \ifx
-comparing temporary macros:
\documentclass{article}
\makeatletter
\newcommand{\foo}[2][Default value]{%
\begingroup
\def\mytempa{#1}%
\def\mytempb{Default value}%
\ifx\mytempa\mytempb
\expandafter\endgroup\expandafter\@secondoftwo
\else
\expandafter\@firstofone
\fi
{%
\def\mytempb{}%
\expandafter\endgroup\ifx\mytempa\mytempb
\expandafter\@secondoftwo
\else
\expandafter\@firstoftwo
\fi
{#1}%
}{#2}~foo(#2)%
}%
\makeatother
\parindent=0ex
\parskip=\bigskipamount
\begin{document}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo{hi}|:\\
\foo{hi}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo[Default value]{hi}|:\\
\foo[Default value]{hi}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo[]{hi}|:\\
\foo[]{hi}
``Default value'' or empty optional argument or no optional argument will be given---%
\verb|\foo[{}]{hi}|:\\
\foo[{}]{hi}
A nice optional argument will be given---%
\verb|\foo[hello]{hi}|:\\
\foo[hello]{hi}
\end{document}

As I said before: In common/usual situations I definitely prefer the \@dblarg
-thing to any of these approaches.
\foo{hello}{hi}
would producehello foo(hello)
since it is called the wrong way!\foo[hello]{hi}
is correct. Please not that you can't use#2
in the argument description at all, so your command is not correct