Test if a given control sequence is an \outer macro

I'm in need of a macro which receives a control sequence as argument to test if this control sequence is actually an \outer macro. I can code it myself as below .

\documentclass{minimal}
\usepackage{expl3}
\ExplSyntaxOn
\group_begin:
\char_set_lccode:nn { \; } { \: }
\char_set_lccode:nn { \Z } { \t }
\char_set_lccode:nn { \T } { \T }
\char_set_lccode:nn { \F } { \F }
\tl_map_function:nN { O U Z E R M A C R ; } \char_set_catcode_other:N
\tl_to_lowercase:n
{
\group_end:
\prg_new_conditional:Nnn \cs_if_outer_macro:c { p, T , F , TF }
{
\exp_last_unbraced:Nf \cs_if_outer_macro_aux:w
\cs_meaning:c {#1} OUZER ~ MACRO ; \q_stop
}
\cs_new_nopar:Npn \cs_if_outer_macro_aux:w #1 OUZER ~ MACRO ; #2 \q_stop
{
\prg_case_str:xxn { #1 ~ }
{
{ \token_to_str:N \ }
{ \prg_return_true: }
{ \token_to_str:N \long \token_to_str:N \ }
{ \prg_return_true: }
{ \token_to_str:N \protected \token_to_str:N \ }
{ \prg_return_true: }
{ \token_to_str:N \protected \token_to_str:N \long \token_to_str:N \ }
{ \prg_return_true: }
}
{ \prg_return_false: }
}
}
\cs_new_eq:NN \IfOuterMacroTF \cs_if_outer_macro:cTF
\ExplSyntaxOff

\long\outer\protected\def\fooa{}
\outer\def\foob{}
\def\fooc{}
\def\food{outer macro:}

\typeout{It's \IfOuterMacroTF{fooa}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{foob}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{fooc}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{food}{}{NOT }an outer macro.}
\typeout{It's \IfOuterMacroTF{relax}{}{NOT }an outer macro.}


It is inspired by the l3token package. The test is expandable. I have used it in the xeCJK package. But, it only receives the csname instead of control sequence. What is more, I think it is a bit slow.

I think it is a good question and like to see other possible approaches.

-
A macro cannot receive an outer token as an argument: TeX will raise an error. –  Joseph Wright May 25 '12 at 18:18
@MarcoDaniel I meant you can't do \outer\def\foo{}\def\baz#1{}\baz\foo, i.e. pass an outer token as an argument. –  Joseph Wright May 25 '12 at 18:53
@QingLee: for completeness about \outer tokens, you also have to be careful about \foo defined by \halign{#\cr\global\futurelet\foo\relax\cr}. Its \meaning is \outer endtemplate: and it indeed is outer. –  Bruno Le Floch Jul 17 '12 at 16:04

This is not expandable, but maybe the test can be modified to be:

\documentclass{article}
\usepackage{expl3,l3regex}

\ExplSyntaxOn
\cs_new_protected:Npn \isouter { \exp_after:wN \checkouter:w \token_to_meaning:N }
\cs_new:Npn \checkouter:w #1 \relax
{
\regex_match:nnTF { outer .* macro .* :-> } { #1 }
{ \typeout{OUTER~MACRO} }
{ \typeout{NON~OUTER~TOKEN} }
}
\ExplSyntaxOff

\outer\def\bye{bye}

\isouter\bye\relax

\stop


However this test can't be used in arguments to other macros, because of the rule mentioned by Joseph Wright.

-
Thakns! But the test will be true when \def\bye{outer macro:->}. –  Qing Lee May 25 '12 at 21:03
@QingLee Yes: I don't think one can do much about this. –  egreg May 25 '12 at 21:05
does Bruno support ^ to anchor the regexp to start of string, in which case you could use that as the trick case would have meaning macro:->outer macro.... –  David Carlisle May 26 '12 at 9:57
@DavidCarlisle yes. –  Bruno Le Floch May 26 '12 at 23:15