Forbidding/requiring certain pgfkeys?

I have some commands whose named arguments are processed by pgfkeys

\dog[breed=Labrador, name=Fido]
\human[name=John, car owned=Porsche]


How can I arrange that

\dog[breed=Labrador, car owned=Rover]


gives the error message Invalid key 'car owned', and

\dog[name=Fido]


gives the error message Missing required key 'breed'?

Here is a MWE to use as a starting point.

\documentclass{article}
\usepackage{pgfkeys}

\makeatletter
\pgfkeys{/wickerson/.cd,
breed/.store in =     {\wickerson@breed},
name/.store in =      {\wickerson@name},
car owned/.store in = {\wickerson@carowned}
}

\newcommand*\dog[1][]{%
\pgfkeys{/wickerson/.cd, name=noName, breed=noBreed, #1}
Dog is a {\wickerson@breed} and is called {\wickerson@name}.\par
}

\newcommand*\human[1][]{%
\pgfkeys{/wickerson/.cd, name=noName, car owned=noCar, #1}
Human is called {\wickerson@name} and owns a {\wickerson@carowned}.\par
}
\makeatother

\begin{document}

\human[name=John, car owned=Porsche]  % GOOD
\dog[name=Fido]                       % BAD (required key missing)

\end{document}

• Welcome to the perplexing world of key filtering :) – percusse May 31 '13 at 13:20
• @percusse Hehe, I was worried that would be the answer. I had a flick through the section on "Filtering" in the TikZ manual earlier, and it all looks rather boggling! – John Wickerson May 31 '13 at 13:23
• Where is the code to declare yours keys? – Paul Gaborit May 31 '13 at 15:06
• @PaulGaborit Ah yes, I should have given you a MWE to play with. See updated question. – John Wickerson May 31 '13 at 15:25
• Rather than using "storage macros" with .store in, you can simply initialize /wickerson/breed/.initial = {}, and then every use of /wickerson/breed = whatever will set the value of this key to whatever, which can be retrieved (expandably) with \pgfkeysvalueof{/wickerson/breed}. This way you don't reinvent the namespace wheel that pgfkeys provides. – Ryan Reich May 31 '13 at 17:34

Converting comment to answer as requested. Be aware that this this is my very first time using a key value system so not sure if there are any problems with this approach but it does satisfy the two requirements of this particular example:

• only valid keys can be specified
• keys that were not specified are flagged as errors

The first requirement is met by defining separate dogkeys and humankeys. For the second requirement we can check that the string value is not the default value to know that it was not provided.

Code:

\documentclass{article}
\usepackage{pgfkeys}
\usepackage{xstring}

\makeatletter
\pgfkeys{/dogkeys/.cd,
breed/.store in =     {\wickerson@breed},
name/.store in =      {\wickerson@name},
}
\pgfkeys{/humankeys/.cd,
name/.store in =      {\wickerson@name},
car owned/.store in = {\wickerson@carowned}
}

\newcommand*\CheckKeyIsNotDefaultValue[4]{%
% #1 = package name (for error reporting)
% #2 = key name
% #3 = default key value (report error if key value matches)
% #4 = actual value of key
\IfStrEq{#4}{#3}{%
\PackageError{#1}{Key '#2' not provided.}{}%
}{}%
}

\newcommand*\dog[1][]{%
\pgfkeys{/dogkeys/.cd, name=noName, breed=noBreed, #1}
\CheckKeyIsNotDefaultValue{dogkeys}{breed}{noBreed}{\wickerson@breed}%
\CheckKeyIsNotDefaultValue{dogkeys}{name}{noName}{\wickerson@name}%
Dog is a {\wickerson@breed} and is called {\wickerson@name}.\par
}

\newcommand*\human[1][]{%
\pgfkeys{/humankeys/.cd, name=noName, car owned=noCar, #1}
\CheckKeyIsNotDefaultValue{humankeys}{name}{noName}{\wickerson@name}%
\CheckKeyIsNotDefaultValue{humankeys}{car owned}{noCar}{\wickerson@carowned}%
Human is called {\wickerson@name} and owns a {\wickerson@carowned}.\par
}
\makeatother

\begin{document}

\human[name=John, car owned=Porsche]   % GOOD
%\dog[breed=Fido]                      % BAD (required key missing)

\end{document}


For forbidding certain keys (or rather, allowing only certain keys), I would define all keys as subkeys of /wickerson/dog and /wickerson/human. You can then also install /wickerson/dog/.unknown key that gets executed when an unknown key is called (though this solution is unaware of whether the key in question would be valid in another context).

The following example throws the error

! Package Wickerson Error: The key "car owned" is not known in the "dog" context..


\documentclass{article}
\usepackage{pgfkeys}

\makeatletter
\pgfkeys{/wickerson/.cd,
dog/breed/.store in =     {\wickerson@breed},
dog/name/.store in =      {\wickerson@name},
dog/.unknown/.code=   {\PackageError{Wickerson}{The key "\pgfkeyscurrentkeyRAW" is not known in the "dog" context.}{}},
human/name/.store in =      {\wickerson@name},
human/car owned/.store in = {\wickerson@carowned},
human/.unknown/.code= {\PackageError{Wickerson}{The key "\pgfkeyscurrentkeyRAW" is not known in the "human" context.}{}},
}

\newcommand*\dog[1][]{%
\pgfkeys{/wickerson/dog/.cd, name=noName, breed=noBreed, #1}
Dog is a {\wickerson@breed} and is called {\wickerson@name}.\par
}

\newcommand*\human[1][]{%
\pgfkeys{/wickerson/human/.cd, name=noName, car owned=noCar, #1}
Human is called {\wickerson@name} and owns a {\wickerson@carowned}.\par
}
\makeatother

\begin{document}

\human[name=John, car owned=Porsche]  % GOOD
\dog[name=Fido]                       % BAD (required key missing)

\end{document}

• I see, yes, this seems nice. So I have to duplicate some the common keys like name ... but that doesn't matter because I can use the same macro \wickerson@name for both. – John Wickerson May 31 '13 at 17:15