Dear SG7,Now this reflector has opened, it's time for me to do the thing I always do and ask the stupid question: is adding reflection to the language a bad idea compared to pushing the problem onto libraries and letting them evolve a superior solution which we can later standardise? I did a similar thing on SG5 Transactional Memory by arguing in favour of dropping language support in favour of exporting just enough for library support (the thread starts at https://groups.google.com/a/isocpp.org/d/msg/tm/zfRyY_XfxSU/47esUzUcHX0J) - I didn't get anywhere, but I still think the question is very useful to ask.
I should also caveat that before I begin, I pitched this idea to Chandler at C++ Now for inclusion into clang, and he was definitely not warm to the idea.My suggestion for implementing compile-time reflection is very simple, and is the absolute minimum possible: the compiler makes available a magic namespace, let's call it namespace __compiler_internals. In this namespace you get a set of pseudo template definitions which happen to make available the present parsing state of the compiler in this compiland. As I originally pitched this
to Chandler regarding clang, I'll stick to a libclang example, but I should emphasise that by this I mean that each compiler makes available its internal state in whatever format it so chooses. It is then up to one or more third party libraries - probably at least one in Boost - to convert compiler-specific namespace __compiler_internals into a unified template API in order to achieve cross-compatibility. I entirely expect such thunk libraries to get repeatedly thrown away and refactored until something emerges which is pretty good for most use cases.
And finally, I only depicted libclang as a first stage example. libclang is of course AST read only. Down the line (not now), one could allow template metaprogramming to rewrite the AST en vivo, so if - for
Anyway, back to you SG7 for consideration. I know I've simply defined the reflection problem into something else, but hey the above ought to work, and it's real easy to do for clang at least as a minimum. The question becomes: is the above solution acceptable for SG7 to recommend for C++17? The trivial answer is probably not, so let me ask another question: if clang implemented the above soon, I can guarantee you that it would be used by Boost libraries within six months and pressure would be brought to bear on the other compiler vendors to do something similar. It is then entirely possible that we'd have a reasonable reflection library in Boost by 2015.
My suggestion for implementing compile-time reflection is very simple, and is the absolute minimum possible: the compiler makes available a magic namespace, let's call it namespace __compiler_internals. In this namespace you get a set of pseudo template definitions which happen to make available the present parsing state of the compiler in this compiland. As I originally pitched thisI'm not a compiler vendor, nor a compiler expert. I have done some minor feature implementations
for the c++ front-end of gcc. Now, with that disclaimer out of the way, I predict implementation
vendors will absolutely balk at this idea. A couple of random remarks:1) _parsing state_? That's not going to help much, unless you think that "parsing state" somehowincludes instantiated templates, and understands the results of non-dependent lookups, and
understands scoping in general.2) parsing state until what? The occurrence of the use of such a pseudo template? Implementationshave certain liberties with the points of instantiation of templates in a translation unit.
If you ask me, this duck doesn't fly - it's dead.
to Chandler regarding clang, I'll stick to a libclang example, but I should emphasise that by this I mean that each compiler makes available its internal state in whatever format it so chooses. It is then up to one or more third party libraries - probably at least one in Boost - to convert compiler-specific namespace __compiler_internals into a unified template API in order to achieve cross-compatibility. I entirely expect such thunk libraries to get repeatedly thrown away and refactored until something emerges which is pretty good for most use cases.Why would we standardize the way to get implementation-specific internals and wait until
an additional library will wrap it into a reasonable portable API? Why wouldn't we just standardize
the portable API?
Anyway, back to you SG7 for consideration. I know I've simply defined the reflection problem into something else, but hey the above ought to work, and it's real easy to do for clang at least as a minimum. The question becomes: is the above solution acceptable for SG7 to recommend for C++17? The trivial answer is probably not, so let me ask another question: if clang implemented the above soon, I can guarantee you that it would be used by Boost libraries within six months and pressure would be brought to bear on the other compiler vendors to do something similar. It is then entirely possible that we'd have a reasonable reflection library in Boost by 2015.I would love to know what makes you think you can guarantee that. With the understandingthat I have about boost, I can almost guarantee the opposite.
I'm not a compiler vendor, nor a compiler expert. I have done some minor feature implementations
for the c++ front-end of gcc. Now, with that disclaimer out of the way, I predict implementation
vendors will absolutely balk at this idea. A couple of random remarks:1) _parsing state_? That's not going to help much, unless you think that "parsing state" somehowincludes instantiated templates, and understands the results of non-dependent lookups, and
understands scoping in general.2) parsing state until what? The occurrence of the use of such a pseudo template? Implementationshave certain liberties with the points of instantiation of templates in a translation unit.
If you ask me, this duck doesn't fly - it's dead.Maybe. I am probably the most familiar with clang out of any of the compiler's internals, but that's like 0.1 on the familiarity out of 10 scale.I think it's very doable if you don't mind treacle like performance loss every time someone touches the
Which may sound brittle and expensive. But is this more brittle and expensive than the compiler vendor implementing machinery for reflection, especially when such implementation may force
behaviours and designs unhelpful to the vendor? For example, if clang and GCC which are AST based come up with one Reflection solution, MSVC which is not AST based (or so I am told) may simply not implement it. That makes standardisation here rather difficult, because standardisation isn't hugely useful when a major vendor doesn't implement it (C99 I'm talking to you).
These are questions, not answers. I don't know the answers to doing Reflection well, let alone usefully. I'm explicitly proposing it's too hard to design now without more experience.
Why would we standardize the way to get implementation-specific internals and wait until
an additional library will wrap it into a reasonable portable API? Why wouldn't we just standardize
the portable API?I am proposing that we standardise acceptance that this is too hard to get right through a design by committee, and we instead push the problem onto library evolution to figure out. I proposed the same
Boost.Mirror, Boost.Puddle et al are good examples of how non-emulated reflection would be a huge help in a huge range of scenarios. Boost.Python could be vastly simplified with reflection. The original
Dear SG7,Now this reflector has opened, it's time for me to do the thing I always do and ask the stupid question: is adding reflection to the language a bad idea compared to pushing the problem onto libraries and letting them evolve a superior solution which we can later standardise? I did a similar thing on SG5 Transactional Memory by arguing in favour of dropping language support in favour of exporting just enough for library support (the thread starts at https://groups.google.com/a/isocpp.org/d/msg/tm/zfRyY_XfxSU/47esUzUcHX0J) - I didn't get anywhere, but I still think the question is very useful to ask.
I think it's very doable if you don't mind treacle like performance loss every time someone touches theYou're welcome to try it. I don't believe it will work, for the reasons I stated above, and likely
more reasons that implementation vendors will likely point out if need be.
Which may sound brittle and expensive. But is this more brittle and expensive than the compiler vendor implementing machinery for reflection, especially when such implementation may force
You're asking for a wild guess. Mine is YES, it's more brittle and more expensive.
I am proposing that we standardise acceptance that this is too hard to get right through a design by committee, and we instead push the problem onto library evolution to figure out. I proposed the sameAnd library evolution will do what? Accept that it's too hard to get right through a "design by
committee" (which is, btw, not necessarily what we're doing here) and give up, like we would do?
Which may sound brittle and expensive. But is this more brittle and expensive than the compiler vendor implementing machinery for reflection, especially when such implementation may force
You're asking for a wild guess. Mine is YES, it's more brittle and more expensive.Maybe. I think clang and gcc are very different beasts to msvc. I also think that reflection is far, far
harder than most realise. I think you need to look into exported templates and ODR violation resolution handling to get a sense of just how hard reflection actually is when pushed to its end. All this feeds into an eventual C++ Modules implementation of course, but Reflection eventually leads there too, and early miscalculations during design can easily lead to stuff having to be deprecated later.
My proposal - if viable - has the huge advantage of letting third party libraries evolve the right way of handling reflection extending to its fullest possible extent. It also includes refactoring designs to cope
Before anyone chips in to say this SG is only looking at compile-time reflection for now, then sure, the previous two paragraphs are off-topic. Note though that my proposal - if it allows the loading of external translation units - lets compile time metaprogramming generate API bindings to remote code at compile-time. That is an enormous win for Component Objects for C++ for example, because it
My concern is that the committee has a lot of language experts who naturally tend towards a mostly language based solution for some feature. Even when a mostly library based solution makes a lot
Have you seen N3815? http://isocpp.org/blog/2013/10/n3815
It's how we are proposing solving introspection of enumeration types. We arrived at the approach you mention. Standardize a complete but minimal low-level direct interface. This allows library authors to build higher-level facilities without having to modify the compiler.
There is a link to my co-authors reflection extensions for clang at the bottom of the proposal. It contains more than just the three queries proposed for standardization.
Have you seen N3815? http://isocpp.org/blog/2013/10/n3815
It's how we are proposing solving introspection of enumeration types. We arrived at the approach you mention. Standardize a complete but minimal low-level direct interface. This allows library authors to build higher-level facilities without having to modify the compiler.
There is a link to my co-authors reflection extensions for clang at the bottom of the proposal. It contains more than just the three queries proposed for standardization.I had seen it, but to be honest I had instantly filtered it out because it seemed to me to apply to enum's only (which didn't seem hugely useful to my needs).Now you've helped me look at it more deeply, I personally speaking don't think your interface is low level enough :) I would personally prefer an interface so low level it is completely compiler version dependent i.e. it can, and does, change arbitrarily.
Now you've helped me look at it more deeply, I personally speaking don't think your interface is low level enough :) I would personally prefer an interface so low level it is completely compiler version dependent i.e. it can, and does, change arbitrarily.
If we isolate our discussion to enumeration types for the moment, it's hard for me to visualize exactly what you mean by a lower-level interface. The three property queries give pretty much direct access to the internal enum-specifier AST. What information do you need about the enum-specifier AST that you can't obtain from our proposed queries, but could obtain from an even lower-level interface?
As the second author of N3815, I am not sure I can follow your explanation. All this talk about a hypothetical enumeration standard/feature set, AST node binding/referencing and void pointer comparisons does not give me an idea how your preferred interface would look like, and to the best of my knowledge all of this is not very relevant to any actual enum implementation in current C++ compilers. This seems like highly dubious "future proofing" at best.
To me, your previously expressed wish to *standardize* a "completely compiler version dependent" e.g. arbitrary(?) interface seems rather paradoxical. You could lobby the individual compiler maintainers for such an interface, but I don't see any role for ISO or SG7 in that effort.
And I'm quite sceptical that this in any way would get us closer to standardized C++ reflection. Anyone serious about creating a reflection interface can simply implement your proposed extensions themselves in a Clang or GCC branch in exactly the form they need for their higher level library API.
But finding good, intuitive interface mechanisms for the typical use cases seems like the biggest problem to me. I am not deeply familiar with the Mirror library, but its heavy reliance on boost::MPL for most non-trivial uses might not be ideal answer for a standardized solution.
A few meta-programming improvements (like Concepts Lite and some proposed support features like better parameter packs etc) to me seem like very important requirements for really good interfaces. I'm afraid (existing) TMP library capabilities are just not good enough for more advanced reflection areas (but enumerations should be doable, as we propose).
As the second author of N3815, I am not sure I can follow your explanation. All this talk about a hypothetical enumeration standard/feature set, AST node binding/referencing and void pointer comparisons does not give me an idea how your preferred interface would look like, and to the best of my knowledge all of this is not very relevant to any actual enum implementation in current C++ compilers. This seems like highly dubious "future proofing" at best.
My original mock up enumerated members of a struct type, and it gave me access to node reference machinery and AST cursors which I feel is desirable. The future proofing is not the point, rather it's
where you decide to put the future proofing: into new language extensions, or into third party abstraction libraries. I was proposing the latter despite that this SG appears to be biased towards the former.
To me, your previously expressed wish to *standardize* a "completely compiler version dependent" e.g. arbitrary(?) interface seems rather paradoxical. You could lobby the individual compiler maintainers for such an interface, but I don't see any role for ISO or SG7 in that effort.
Not at all. There are plenty of areas in C++ where the standard explicitly standardises intentionally no standard. A perfectly valid outcome from SG7 deliberations is to recommend to the committee to do nothing, or more likely to do some particularly useful variant of doing nothing.
But finding good, intuitive interface mechanisms for the typical use cases seems like the biggest problem to me. I am not deeply familiar with the Mirror library, but its heavy reliance on boost::MPL for most non-trivial uses might not be ideal answer for a standardized solution.
Mirror's API isn't bad, it's more that it needs a post compilation step to add in the reflection metadata which is annoying. And actually the MPL is probably one of the most core tools anyone using Boost uses - I don't think I've written much code in the past five years which didn't use the MPL. Once Boost has figured out how/whether to refactor MPL for C++11, I'm very sure MPL will enter the standard as it's incredibly useful.
A few meta-programming improvements (like Concepts Lite and some proposed support features like better parameter packs etc) to me seem like very important requirements for really good interfaces. I'm afraid (existing) TMP library capabilities are just not good enough for more advanced reflection areas (but enumerations should be doable, as we propose).On this we agree. I'm also asserting that we simply don't have sufficient experience to design good language extensions right now, and we ought to demur to a library evolution approach.
To me, your previously expressed wish to *standardize* a "completely compiler version dependent" e.g. arbitrary(?) interface seems rather paradoxical. You could lobby the individual compiler maintainers for such an interface, but I don't see any role for ISO or SG7 in that effort.
Not at all. There are plenty of areas in C++ where the standard explicitly standardises intentionally no standard. A perfectly valid outcome from SG7 deliberations is to recommend to the committee to do nothing, or more likely to do some particularly useful variant of doing nothing.What you have proposed is not a variant of doing nothing. It's worse than doing nothing.
On this we agree. I'm also asserting that we simply don't have sufficient experience to design good language extensions right now, and we ought to demur to a library evolution approach.Based on that assertion we don't have sufficient experience to decide that this "libraryevolution" approach is any better. Not that the assertion is valid anyway - we have plenty
of experience from google protobuf, boost fusion, Qt, and other existing attempts at
reflection to know what we want to achieve, and a good part of how.
If it's completely implementation-specific, a valid exposure is to expose absolutely
nothing.
If it's not implementation-specific, we're already well on the way to standardize
an actual reflection API. The implementation-specific approach will accomplish absolutelynothing and will only hinder the writing of portable code using reflection.
What you have proposed is not a variant of doing nothing. It's worse than doing nothing.In your opinion Ville. I would view my proposal as an entirely valid proposal for C++1x as an
information and good practice gathering exercise. I agree that long term it isn't ideal, but that's the point of it being an information and good practice gathering exercise. I never claimed - despite your best attempts to paint me so - that this proposal is for all future C++ standards. No, it's an interim measure for the near future, and I have been clear about that since the beginning.
On this we agree. I'm also asserting that we simply don't have sufficient experience to design good language extensions right now, and we ought to demur to a library evolution approach.Based on that assertion we don't have sufficient experience to decide that this "libraryevolution" approach is any better. Not that the assertion is valid anyway - we have plenty
of experience from google protobuf, boost fusion, Qt, and other existing attempts at
reflection to know what we want to achieve, and a good part of how.All of which have filled me with considerable caution, because all of those proposals do not scale well, especially in the presence of C++ Modules and ODR violation resolution strategies. I freely admit I don't know which design would be good, but I am very certain that all these designs you just mentioned are deeply flawed when used at scale.
If it's completely implementation-specific, a valid exposure is to expose absolutely
nothing.Correct. Another valid exposure is to supply a copy of libclang with every compiler, such that every compiler gets libclang bindings mapped into its compile-time space. Reflected AST parsing now becomes completely external to the current compiland. This, I would imagine, would be the easiest path for GCC and MSVC, perhaps even clang itself.
If it's not implementation-specific, we're already well on the way to standardize
an actual reflection API. The implementation-specific approach will accomplish absolutelynothing and will only hinder the writing of portable code using reflection.By defining Reflection to be compile-time only for now, we have defined away much of the difficulty in getting a good Reflection design right. Equally that means the present Reflection support will only EVER be for compile-time use only.
Which might actually be fine for C++-1y - this SG proposes some support and it is explicitly aimed for compile-time use and never anything more. In which case, my proposal still stands as a valid
proposal for gaining information and good practice gathering for a Reflection implementation which does support dynamic Reflection after C++-1y.It is therefore STILL a worthwhile item for this SG to consider, especially as a library based design can be evolved far faster than an ISO standard.
I'm not trying to paint you anything. I'm saying that this idea is a step in the wrong direction,
and having it as an interim measure is not very useful.
I have no reason to believe that an implementation-specific solution on top of whichlibraries have to be built to support every different implementation is in any way superior.
It would be helpful to know how and why you think the aforementioned designs are"deeply flawed when used at scale". I have a couple of reasons why they are flawed;protobuf relies on external code-generation tools, boost fusion requires a very
intrusive and tedious adaptation to be written, and Qt assumes a very specificopt-in class hierarchy with considerable overhead. We know those issues, too,
and the plan of this SG is to avoid those problems from the get-go. Sticking our
head into the sand and hoping that an implementation-specific solution with 3rd party
libraries on top of it will not solve any of those problems, at all.
That sounds like standardizing libclang. Perhaps that would be a possible outcome, but Ihighly doubt it. I don't think exposing an AST of any kind is a reasonable first step.
It is therefore STILL a worthwhile item for this SG to consider, especially as a library based design can be evolved far faster than an ISO standard.A language TS can evolve at a speed equal to a library TS.
I'm not trying to paint you anything. I'm saying that this idea is a step in the wrong direction,
and having it as an interim measure is not very useful.I agree it is not as useful for simple compile-time reflection as less ambitious solutions. I think your judgement here is based on the immediate needs of C++-1y - and I have no problem with that BTW, so long as it is recognised that a simple compile-time only design will only be intended as such.
I have no reason to believe that an implementation-specific solution on top of which
libraries have to be built to support every different implementation is in any way superior.Depends on scope of use case. If you push Reflection far enough, it inevitably enters implementation specificness anyway.
This is off topic for this SG's stated intent for 1y, but my concern lies in how to sanely reflect reflecting code, reflect the reflection of reflection, and then onto infinity of recursion, while operating in an environment with C++ Modules where a definition in one module might be subtly different (in a segmentation fault causing kind of way) from another definition in another module. Part of the difficulty here is that probably the link stage will have to late stage code generate according to cross-compiland Reflection (e.g. if module 1 reflects module 2 which reflects module 1 etc) which implies the need for storing not just a program's intermediate state to be traversed during link, but a mutable (yes, mutable) database of C++ compilands which can be queried for link and type reflection. Right now, I can't think of a "good" way of handling this, but I'm fairly sure that exposing the compiler's internal state to the code being compiled is a reasonable start.
You must remember where I'm coming from and my use case: getting some precompiled C++ blob of LLVM bytecode to load and interoperate with many other and unknown precompiled C++ blobs of LLVM bytecode even if they were compiled with different STL or Boost implementations. I'm thinking of a world where you simply don't have to recompile C++ blobs of LLVM bytecode even if the world surrounding them changes, because the C++ dynamic runtime and loader can see what needs doing and can cope.You might say "this is an extreme case that will never materialise, and therefore isn't important here". And maybe it won't. Equally it's a very useful ad absurdum thought exercise for getting Python or Ruby
or Java runtimes to far more seamlessly interoperate with some arbitrary chunk of C++, because if C++ can interoperate with C++, I would argue that any other language will find it much easier to do so as well. Equally, it's hard for C++ to argue for its increased usage as a systems programming language if it can't even interoperate with itself.
That sounds like standardizing libclang. Perhaps that would be a possible outcome, but I
highly doubt it. I don't think exposing an AST of any kind is a reasonable first step.From my own work I believe that even an AST for every compiland is insufficient for the kind of Reflection I just described. We in fact need all the ASTs, and then some more metadata again (i.e. new C++ keywords to disambiguate what the programmer really means by "class Foo" for example).
It is therefore STILL a worthwhile item for this SG to consider, especially as a library based design can be evolved far faster than an ISO standard.A language TS can evolve at a speed equal to a library TS.
Who said anything about TSs? I have been clear since the beginning that third party libraries would
evolve in whatever way they think best outside of ISO. Sometime later, when we have some idea of some viable design, then we might think about ISO standardisation. For the kind of reflection I was talking about earlier, this is almost certainly a C++-2x worry. Right now we have no idea what we don't know.
My suggestion for implementing compile-time reflection is very simple, and is the absolute minimum possible: the compiler makes available a magic namespace, let's call it namespace __compiler_internals. In this namespace you get a set of pseudo template definitions which happen to make available the present parsing state of the compiler in this compiland. As I originally pitched this to Chandler regarding clang, I'll stick to a libclang example, but I should emphasise that by this I mean that each compiler makes available its internal state in whatever format it so chooses. It is then up to one or more third party libraries - probably at least one in Boost - to convert compiler-specific namespace __compiler_internals into a unified template API in order to achieve cross-compatibility. I entirely expect such thunk libraries to get repeatedly thrown away and refactored until something emerges which is pretty good for most use cases.
I daresay I disagree. Many people in this SG have a very good idea what they know and what is more unknown, and we have a pretty good idea how to solve the first evolutionary steps so that the subsequent steps are still possible. And your proposal doesn't fit at least my mental image of what we should do, when, and how. It's a proposal that takes a completely different direction, and I believe that direction would be a mistaken one.
Note that I might think quite differently if there were existing practice of implementations even remotely agreeing on reflection-like facilities. Currently there's next to none of that, so it may fall as a task of this SG to find common ground and plow the way.
I'm well known for proposing completely different directions, even if I know they stand no chance (as I knew before I started this thread). I believe they can help solidify thinking in others, and my thanks to you Ville for engaging. Also, if you don't turn up to WG21 meetings then generally any proposition or opinion you have tends to fall by the wayside (and correctly so I think), so SGs such as this and
It should be ok to trigger the extraction of an existing class that didn't
declare itself with any special marker. However, how do we ensure that we
don't bloat executables at runtime by duplicating information everywhere?
Classes should be able to opt-in to having the information in a canonical
place.
> > Should weinclude inline functions?
>
> Of course yes.
But they may not be callable.
Incidentally, this may not work at all for template classes. Some member
cannot be instantiated, and thus may not be callable. Then again, the meta
data should just include the fact that instantiation failed.
> > Should we include typedefs and nested structs?
>
> For example to create automatic serialization the type of all fields will
> be necessary.
> So if a field type is a nested struct then this information will be
> necessary to access it own fileds.
> Not sure about typedefs.
The member variables yes, but I'm asking about typedefs and nested structs
that may not be used at all. For example:
> > if the class is a template, should it include the template parameter
>
> list?
> Probably no, this would be too complicated to handle.
So probably we can extract information about full template instantiations, but
not about the templates themselves or partial specialisations.
> > Finally, there will be issues with access protection.
>
> Yes this is a issue. You can not properly serialize a class if you can not
> ready from or write to all of it fields.
>
> Apparently some extra meta-data is also necessary in some cases.
>
> struct my_data
> {
> int *array [[reflect::is_array_ptr]];
> int array_length [[reflect::is_array_length]];
> float value [[reflect::range(0.01, 20.0)]];
> };
>
>
> Again the problem with this approach is that it is intrusive.
Right. That's why the class needs to declare its reflection metadata by itself.
I don't see how you could elsewhere just declare that the range of the value
is 0.01 to 20.0. And what happens if it got declared differently elsewhere?
Program is ill-formed?
> Right now my favorite solution are a couple of compiler intrinsics that
> will give you all this information.
>
> For example t extract information about member function flowing intrinsics
> can be used.
>
> __record_method_count(T) -> size_t how many member function
> class/struct T contains
> __record_method_type(T,I) -> Type type of I'th member function
> __record_method_identifier(T,I) -> string name of the I'th member
> function
>
> Now this intrinsics can be warped into a library.
>
> reflect::field<my_data>::size == 3
> reflect::method<my_data>::size == 0
Right.
I'm thinking a bit lower-level, though. How would this information be stored
in memory? A good paper should provide implementation hints, to prove that
this is feasible, like we've done for the enums. And IMHO it should also prove
that this information can be mostly relocation-free.
I also want this generic, not a template. I want something like:
void dump(const std::typeinfo *ti)
{
template<class T, size_t... I>
void print_methods_impl(index_sequence<I...>) {
const char* names[] = { __record_method_identifier(T, I)... };
index_sequence from C++14 and nothing else.
Not it is not easy to understand way of reflection.
This is only low level way of reflection that can be used to build library on it.
> I also prefer the compile time reflection way, but I don't like this so complicate template meta programming - vararg style hack.
This is not a hack this are only variadic templates andindex_sequencefrom C++14 and nothing else.
> I want to get an constexpr AST node object from a (template)parameter in a clean way.
How, can you please be more concrete?
// origin
enum class EVote
{
Yes,
RatherYes,
Undecided,
RatherNo,
No
};
// driver
class EnumDriver
{
public:
constexpr EnumDriver(const EnumDecl& enumDecl)
{
for (auto& enumerator : enumDecl.enumerators())
enumValueNames.push_back(enumerator.getName());
}
// meta::vector is a constexpr vector
meta::vector<meta::id_name> enumValueNames;
};
// template with attached driver
template<typename T> $use(EnumDriver driver)
void Json::readFrom(T& obj, const std::string& data)
{
obj = llvm::StringSwitch<T>(data)
$for (auto enumValueName : driver.enumValueNames) {
.Case($enumValueName.asStr(), $enumValueName) }
;
}
// usage
int main()
{
Evote vote;
Json::readFrom(vote, "EVote::Undecided");
return 0;
}
So with the $usekeyword you will got an AST node.
For me this code is looking more complicated as my example.
It also uses some Clangs/LLVM internals that other compiler just can not use.
The idea is just to add some new intrinsic to the compiler and no new keywords like $use.
> Really? At least i does not use template meta programming and varargs.
This was not template meta programming but only template programming and not varargs but variadic templates from C++11.
But I agree variadic templates can look strange if you are not familiar with it.
> No, I don't want to use clang internals, only a standardized interface of it.
But how to make such standardized interface for whole AST ?
If you look at Clang AST then it is not really easy to create simplified interface to it.
> But it does not leads to a general compile time reflection solution. You can not generate code with it.
Yes it is not general compiler time reflection. To be more precise it is compiler time introspection.
But I think it should be possible to generate "code" using it and templates.
> But as a temporary solution I can live with it, at least it is better than nothing...
Yes this can be temporal solution or better first step of the solution.
Right now all this intrinsics and more are already implemented and working in experimental branch of Clang.
There is not proper library for it but it already can be used in the code.
So the example above can be compiled and work right now.
// full grammar is here: http://www.json.org/
struct JsonParamGrammarItem : meta::grammar
{
meta::id_name key;
meta::op_name colon = ':';
meta::expr value;
};
struct JsonParamGrammarTail : meta::grammar
{
meta::id_name comma = ',';
JsonParamGrammarItem item;
};
struct JsonParamGrammar : meta::grammar
{
meta::symbol open_brace = '{';
JsonParamGrammarItem paramFirst;
meta::vector<JsonParamGrammarTail> paramMore; // this can be used for varargs ...
meta::symbol close_brace = '}';
};
class JsonParamDriver
{
JsonParamDriver(JsonParamGrammar grammar);
};
/*
grammar rules
or - meta::or
and - member in struct
optional - meta::optional
any - meta::vector
so JsonParamGrammarItem is
meta::id_name & ':' & meta::expr
*/
// usage
class SomeWidget
{
template<astnode Node>
SomeWidget(Node) $use(JsonParamDriver driver)
{
...
// We should process with an other driver that associate member names with param names.
}
SomeWindow window;
SomeLabel label;
};
SomeWidget widget({
window: "Hello world",
label: "Foo"
});
> It looks horrible! IMHO, we should deprecate the whole template programming
So what you want is to deprecate half of the C++ ?
Then probably you need to look at another language, may be D ?
IMHO we should first create working and better alternative before deprecated anything.
And we should first start with PREPROCESSOR, but this is off topic now.