Type-mapping is hard. Consumers want to use their favorite types with as little trouble as possible. Unfortunately with C++ this means the bindings generator has to generate code which knows about those types. For example, you have a mojom:
interface URLLoaderFactory {
CreateURLLoader(url.mojom.Url url, URLLoader& request);
};
The author wants users to be able to call CreateURLLoader in C++ code using a GURL, and wants implementations of URLLoaderFactory to be able to expect a const GURL& argument; i.e. the C++ interface we generate must be:
class URLLoaderFactory {
virtual void CreateURLLoader(const GURL& url, ...) = 0;
};
This unfortunately means that the mapping of url.mojom.Url=>GURL *must* be known at bindings generation time, and it also means we must support only one kind of type-mapping for url.mojom.Url.
This is effectively why we have bindings variants, so we generate one version for Blink which uses one global configuration (and e.g. maps all url.mojom.Url everywhere to KRUL), and another version for all other code which uses another global configuration (and e.g. maps all url.mojom.Url everywhere to GURL).
It would be a pretty huge reduction in build and code complexity if we could find a way to avoid these constraints and ultimately get rid of typemap variants and the global configuration(s) altogether.
There are a few ideas we've considered or previously discussed for accomplishing this. They could use more consideration, and other possibilities should also be dreamt up.
1. For one option, we could consider a message builder-style API, where instead of generating pure virtual interfaces, we generate message builders with per-parameter builder methods. e.g. (modulo verbosity):
class URLLoaderFactory_CreateURLLoader_Message {
public:
URLLoaderFactory_CreateURLLoader_Message();
template <typename MaybeConstRefUserType>
URLLoaderFactory_CreateURLLoader_Message WithURL(MaybeConstRefUserType&& url) && {
// template goop to capture (for lazy serialization) or serialize |url| as
// a mojo.mojom.Url using user-defined mojom traits
}
URLLoaderFactory_CreateURLLoader_Message WithRequest(URLLoadRequest request) && {
// ...
}
};
The tricky parts here are the extra verbosity of user and impl code, manually managing inclusion of headers which define traits when needed, and generating code which is (unlikely our simpler but more constrained virtual interfaces today) mostly impossible for casual readers to comprehend.
2. For a second option we could consider C++20 (formerly C++17... sadface) "concepts", which would allow for us to retain our generated class interface style while getting the flexibility of the above builder approach, as we can represent e.g. mojom struct types as C++ concepts, and then say something like:
class URLLoaderFactory {
virtual void CreateURLLoader(Url&& url, URLLoadRequest request) = 0;
};
The Url concept would accept any type T which has a known mojo::StructTraits<T, url::mojom::Url> specialization.
This would be great, but it's C++20.