Open JohelEGP opened 6 months ago
The design paper will come later.
I opened #909 for the design write-up.
Now reflect.h2
doesn't have a dependency on parse.h
.
It is compiled as a pure Cpp2 header, taking advantage of https://github.com/hsutter/cppfront/issues/594#issuecomment-1793627053.
reflect_impl.h2
has the remaining bits that depend on parse.h
.
To regenerate reflect.h2
, use
cppfront -p reflect.h2 -o cpp2reflect.h
mv cpp2reflect.h ../include/
I use std::any
values to build the compilation firewall.
The implementation, cpp2reflect.hpp
, is #include
d at the end of reflect_impl.h2
.
This is why odr-uses of the reflection API requires linking to cppfront
.
If depending on Boost.DLL is undesirable, loading and using shared libraries is actually quite simple:
For POSIX:
dlopen
to open the SOdlclose
to close the SOdlsym
to get a symbol (function in this case)dlerror
to get a error string for error checkingFor Windows:
LoadLibraryA
to open the DLLFreeLibrary
to close the DLLGetProcAddress
to get a symbol (again, a function)GetLastError
, FormatMessageA
and LocalFree
to get a error string for error checkingIt doesn't get much more complicated than that if you just want to call C-named functions. Let me know if you'd like me to support on this.
Let me know if you'd like me to support on this.
Thank you. I think we would all like this.
It would help to not have to depend on Boost.DLL if there is an implementation for the current platform.
For GCC, I can use my system's Boost.DLL, But for Clang, I have to build its dependencies from source due to ABI (https://github.com/hsutter/cppfront/discussions/797#discussioncomment-7492847).
Alright, I'll write the changes and open PR against the branch in your repo, we can discuss further over there once its up 👍🏻
The diff in QtCreator better shows how reflect.h2
changed into cpp2reflect.h2
:
Thanks to the contribution of https://github.com/JohelEGP/cppfront/pull/1 by @DyXel and @edo9300
now we directly use the OS APIs for loading libraries.
This means that Boost.DLL is no longer required, and
a cppfront
compiled as usual will have support for loading metafunctions.
You still need to specify the libraries with metafunctions via an environment variable.
I have updated the opening comment, which should be used as commit message when merging, to reflect this.
There's an issue with regards to symbol visibility.
On Windows, symbols are not exported by default. For Visual Studio, we need to use one of four methods to export symbols (https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function?view=msvc-170).
In Cpp1, exporting a symbol usually entails decorating its declaration with a portable macro (here CPPFRONTAPI
).
Unfortunately, there's no way to specify that in the Cpp2 source reflect.h2
.
The other three methods for VS happen outside the source code, which means manually listing the declarations.
Unlike GCC and Clang, it seems like VS doesn't offer an option to export all symbols. So we need Cpp2 support to export library symbols, or to use one of the other three VS-specific methods, or, equivalently, a patch that applies the export macro to the generated declarations.
NB: It is compiling cppfront
which should export the symbols
as it compiles the definitions in cpp2reflect.hpp
.
So we need Cpp2 support to export library symbols,
I have another use for such a feature.
In my case, I'm using an implementation of @rule_of_zero
(https://github.com/hsutter/cppfront/pull/808).
Then I'm defining @sfml
, which uses it (https://github.com/hsutter/cppfront/discussions/789#discussioncomment-7572313).
When compiling with the hidden visibility preset, loading the metafunction sfml
fails.
It works when compiling the module that declares sfml
because it imports the module that exports rule_of_zero
.
But on load, the rule_of_zero
in its body is diagnosed as an undefined symbol, as it isn't visible.
Manually adding CPPFRONTAPI
to the lowered declaration of rule_of_zero
makes it work.
I'm thinking of just adding a metafunction to lower a declaration with the CPPFRONTAPI
macro.
There are similarities to export
declarations of C++ modules
(which will be an access-specifier in Cpp2, see https://github.com/hsutter/cppfront/issues/269#issuecomment-1464790572).
I asked on the Cpplang Slack on how it relates to symbol visibility, but they are orthogonal features
(see https://cpplang.slack.com/archives/C92GZLCSE/p1700658714626929).
Relatedly, we might eventually want to lower extern "C"
declarations.
@c_api
for something else.Anyways, I'll try to think of some fitting name to get things moving here. Suggestions are welcome!
When compiling with the hidden visibility preset, loading the metafunction
sfml
fails. It works when compiling the module that declaressfml
because it imports the module that exportsrule_of_zero
. But on load, therule_of_zero
in its body is diagnosed as an undefined symbol, as it isn't visible. Manually addingCPPFRONTAPI
to the lowered declaration ofrule_of_zero
makes it work.
I guess technically you could use the C declaration for this case, but you'd need to do the casting to void*
, plus namespace handling would be out of the window. Or, since you can detect the signature of a metafunction already, metafunctions detected within a body of another could be lowered to the specific C-magic. Both very ugly hacks but could work for a POC.
Indeed there needs to be a way to mark stuff to use C-linkage and/or declare its symbol visibility (in general, a way of spelling this specific stuff, I am sure there are more out there), but I wonder, should that functionality be covered as you mention with a metafunction (c_api
)? I didn't think of using them like that, feels like that is not what metafunctions are intended for, and you are still modifying the signature of a function within cpp2 limits, no? It should error out saying its not valid code.
Edit: Saw the commit, of course adding a flag to modify the lowering behavior, that works! But I still am questioning whether metafunctions are the right tool for this.
With commit 7107644ead0d837307d1cd5183119258e11c1938,
by declaring rule_of_zero
with @visible
when using the hidden visibility preset,
sfml
loads successfully and everything works as when using the default visibility preset.
Now I just need to overload if for types,
use it in reflect.h2
,
and then VS will work (https://github.com/hsutter/cppfront/pull/907#issuecomment-1870757843).
I guess technically you could use the C declaration for this case, but you'd need to do the casting to
void*
, plus namespace handling would be out of the window. Or, since you can detect the signature of a metafunction already, metafunctions detected within a body of another could be lowered to the specific C-magic. Both very ugly hacks but could work for a POC.
The undefined symbol being that of a metafunction is a coincidence.
The general issue is that Cpp2, without @visible
, can't be used to author a DLL
(with proper hygiene, i.e., hidden visibility by default, like the Windows default).
A metafunction couldn't use a name declared in another Cpp2 TU.
Now I can use a cppfront
compiled with the hidden visibility preset.
With it, my uses of metafunctions in my project work just fine.
So the Visual Studio issue should be solved now (https://github.com/hsutter/cppfront/pull/907#issuecomment-1870757843).
The windows CI
Warns (https://github.com/JohelEGP/cppfront/actions/runs/7351541625/job/20015062242#step:5:68):
reflect.h2(81): warning C4251: 'cpp2::meta::compilerservices::data': class 'std::any' needs to have dll-interface to be used by clients of class 'cpp2::meta::compiler_services' C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.37.32822\include\any(108): note: see declaration of 'std::any' reflect.h2(305): warning C4275: non dll-interface class 'cpp2::meta::declaration_base' used as base for dll-interface class 'cpp2::meta::declaration' reflect.h2(262): note: see declaration of 'cpp2::meta::declaration_base' reflect.h2(304): note: see declaration of 'cpp2::meta::declaration'
The first, I can't solve. I suppose it isn't a problem.
Errors (https://github.com/JohelEGP/cppfront/actions/runs/7351541625/job/20015062242#step:5:100):
reflect.h2(693): error C2375: 'cpp2::meta::interface': redefinition; different linkage reflect.h2(693): note: see declaration of 'cpp2::meta::interface'
And so on for the other declarations. I suppose I should also lower the macro to the non-defining declarations.
I have a plan to solve the name lookup problem for good using only the current source file.
@
-used metafunction using the source order name lookup
(since source_order_name_lookup
is in to_cpp1.h
, and
sema.h
's get_declaration_of
isn't of help here,
I'll have to repeat source_order_name_lookup
during parse.h
).
If the lookup isn't locally unambiguous, I can diagnose why and how to fix it (there are many things to consider).static_assert
to confirm that Cpp1 lookup finds the name we found.
If you can @
-use it, you should be able to use it (even if not strictly necessary by the implementation).The only chance for surprise is when a user expects a non-local name to be found.
In the rare case we have a local match, the generated code won't be what the user expects.
That error shouldn't get past the static_assert
.
But it might just immediately break evaluating the next metafunction in a chain.
I need the metafunction symbols exported by the libraries. I will fall back to using Boost.DLL to prototype the solution. It would be painful to use the C interfaces: https://stackoverflow.com/a/2694373.
Or maybe I should just go ahead and start emitting the extra semantic information (https://github.com/hsutter/cppfront/issues/909#issuecomment-1871286126).
Or maybe I should just go ahead and start emitting the extra semantic information (#909 (comment)).
I think having a specific C function per TO/DLL that is able to tell whether or not it has the symbol, has value on its own, for example:
CPP2_C_API int cpp2_meta_library_has_metafunction(const char* name, size_t size) {
static std::set<std::string_view> mfs = {"greeter", /*...*/};
return mfs.count(std::string_view{name, size});
}
(...or alternatively, a function that gives you a list of strings from which you can build a look-up table)
Armed with a function like this you would be able to tell what was exported, but also, you could first check the existence of this same function before proceeding with anything else, granting the opportunity to give the user a good explanatory message, like "cpp2_meta_library_has_metafunction
was not found in DLL 'x', are you sure 'x' is a cppfront meta library?".
Just my 2 cents though.
Great idea, thank you!
but I wonder, should that functionality be covered as you mention with a metafunction (
c_api
)? I didn't think of using them like that, feels like that is not what metafunctions are intended for
I don't disagree.
But it's currently the most fitting place to specify properties for the declaration.
I also want a @deleted
instead of having to unsatisfactorily abuse a private
overload
(see the thread starting at https://github.com/hsutter/cppfront/issues/468#issuecomment-1627565647 and the referencing issues).
And potentially @all_freestanding
, @freestanding
, @freestanding_deleted
, and @hosted
(whose effect depend on __STDC_HOSTED__
).
Although those have usability limitations (it might not really be what we want):
More generally, we still need a replacement for some uses of the preprocessor. Maybe Cpp1 reflection will help here.
I don't disagree. But it's currently the most fitting place to specify properties for the declaration.
Yeah, for a POC is fine. I do want people trying this functionality to its maximum and give good feedback to Herb, but I do worry about its future, been thinking about this for a while now so I might as well share my opinion:
A user-defined metafunction right now can do way too much, after all, it is arbitrary Cpp1 code being compiled and executed. It was fine being only used internally by the cppfront compiler, but if we give users total freedom to do whatever they want within a metafunction (e.g. execute arbitrary code and generate side-effects) then it'll become a problem once cpp1 reflection/generation lands, assuming it would be a subset of what cppfront offers, there would be competition between what cppfront can do and what was standardized ("teach a man how to fish...").
Another thing that I don't like is the necessary double-pass introduced by this user-defined meta functionality, in order to use a user-defined metafunction you'd need to write Cpp2, which gets lowered to Cpp1, which then gets compiled, and then used somewhere else in Cpp2, this extends the usage from simple transpiler (cpp2
-> cpp1
-> compile and run!
) to something more complicated (cpp2
-> cpp1
-> compile meta
-> cpp2
-> cpp1
-> compile and run!
), which might drive adoption away.
For the latter, I think in a perfect world, cppfront would be able to interpret and apply the metafunction itself without having to loopback, and then when cpp1 meta lands (hopefully a good implementation with feedback received from this experiment!), we start generating cpp1 code instead of interpreting, and so users would be minimally affected.
then it'll become a problem once cpp1 reflection/generation lands, assuming it would be a subset of what cppfront offers, there would be competition between what cppfront can do and what was standardized
For C++26 (P2996), the feature sets would be disjoint. In the future, we should have metafunctions (meta classes?) in Cpp1. That should subsume Cpp2 metafunctions, and we should migrate to using that Cpp1 feature.
Or maybe I should just go ahead and start emitting the extra semantic information (#909 (comment)).
I think having a specific C function per TO/DLL that is able to tell whether or not it has the symbol, has value on its own, for example:
CPP2_C_API int cpp2_meta_library_has_metafunction(const char* name, size_t size)
Now I need this function to have a unique name per Cpp2 source with metafunctions.
A library can be composed of multiple source files. So I can't use the library path for the unique name. When loading the library, I can't map back to its source files.
I could use the absolute path of the Cppx source file.
But without the abstraction to loop over a DLL's symbols,
on top of needing the library path when loading a metafunction,
I would also need the Cppx source files that make it up.
The source of this information would be the caller of cppfront
.
Emitting extra semantic information would be cleaner and forward-looking (https://github.com/hsutter/cppfront/issues/909#issuecomment-1871286126).
Of course, that still requires the caller of cppfront
to give us a file
and to forward that file to the compilation of dependent Cpp2 source files.
- Emit a
static_assert
to confirm that Cpp1 lookup finds the name we found. If you can@
-use it, you should be able to use it (even if not strictly necessary by the implementation). -- https://github.com/hsutter/cppfront/pull/907#issuecomment-1871675196
For this sanity check to be viable in a non-modules world,
metafunctions need to be authored in a pure Cpp2 .h2
header.
The implementing .hpp
header needs to be #include
d in a library's source file.
A metafunction's @
-use requires its .h2
header to be #include
d and its implementing library to be linked to.
See https://github.com/hsutter/cppfront/issues/594#issuecomment-1793627053 for details on this .h2
header usage.
I would also need to emit the loadable symbol in Phase 2 "Cpp2 type definitions and function declarations".
With regards to https://github.com/hsutter/cppfront/pull/907#issuecomment-1872644205.
There's another way to support multi-source libraries/C++ modules with TMP.
The function that returns the list of symbols would be named the same on all sources
but be a template dependent on a compile-time counter.
The source compiled with CPPFRONT_METAFUNCTION_LIBRARY
(the Cpp2 source of the library which includes the implementing .hpp
headers,
and the module interface unit for a C++ module)
would have the uniquely-named symbol that returns the aggregated list.
@hsutter This is ready for review.
This is what might be able to remove the need for CPPFRONT_METAFUNCTION_LIBRARY
(https://github.com/hsutter/cppfront/issues/909#issuecomment-1885005866):
IIUC, that inverts the logic so that plugins register themselves, right?
Yes, mostly. The application still needs to know that libraries to load but this process is just reduced to system calls to load the library and find one "C" function with a known name.
This is what might be able to remove the need for
CPPFRONT_METAFUNCTION_LIBRARY
(#909 (comment)):IIUC, that inverts the logic so that plugins register themselves, right?
Yes, mostly. The application still needs to know that libraries to load but this process is just reduced to system calls to load the library and find one "C" function with a known name.
https://github.com/hsutter/cppfront/pull/907#issuecomment-1871737914 I briefly mentioned something similar here.
Thinking about it, what you'd need is a static object for which you can register the metafunctions automatically when the DLL is loaded, then you can have a per-DLL function with that unique name that gives you back the mapping between a name and the actual metafunction. it would also be a good place (needed even?) for a "teardown", as we discussed.
Regarding the above compiler flags:
-rdynamic
, --export-dynamic
: Seems like one of these is redundant according to the documentation. -fPIC
: It's just as I mentioned during our call: It's good practice to activate for SOs in order to avoid problems when loading multiple SOs that could be on the same memory layout, but it is not strictly required (ref). Windows' DLLs have the equivalent by default called File Base Relocations, so no problems there.-shared
: Seems like its necessary since the compiler won't assume you want to produce a SO just from the output filename. Importantly, it says that you should use the same linker flags if you have multiple steps (compile and then link) for predictable results (ref), shouldn't be a concern for us if we aren't doing anything fancy inside a script. MSVC and such have the equivalent flag /LD
.-soname
: If I understood this SO answer properly, seems like the system needs it to know where to find a dependency symbol, but I think we shouldn't really need this unless we start talking about inter-dependencies from one metafunction library to another (To be discussed further?). In short, since we are manually looking up symbols with dlopen
, I don't think it affects us yet and can thus be skipped. No equivalent/unnecessary on Windows.By the way, as a side-note: Could we please change the envvar names a little? I got them mixed up during our call (sorry for that), though the conclusion was correct, I think they are too similar:
CPPFRONT_METAFUNCTION_LIBRARY
CPPFRONT_METAFUNCTION_LIBRARIES
But I don't know what to suggest, maybe CPPFRONT_META_LIB_NAME
and CPPFRONT_META_LOAD_LIBS
?
I also checked the workflow on my machine.
-rdynamic
is necessary. Without it, the symbols of cppfront are not exported and the libraries can not find the cppfront API.
The error is:
main.cpp2...Contract violation: failed to load DLL './libmetafunctions.so': ./libmetafunctions.so: undefined symbol: _ZNR4cpp24meta16type_declaration10add_memberERKSt17basic_string_viewIcSt11char_traitsIcEE
CPPFRONT_METAFUNCTION_LIBRARY
is really required and it is not. Each library can have the same function and it will not collide on the dlopen
call. Nevertheless, from your comment in reflect_impl.h2
:
// FIXME Doesn't work for a library with more than one source providing a metafunction
// See https://github.com/hsutter/cppfront/pull/907#issuecomment-1872644205
I understand that you wanted to fix the situation if you have e.g. two files meta1.cpp2
and meta2.cpp2
both containing a metafunction. Then you need two calls to cppfront and cppfront will inject two times the function cpp2_metafunction_get_symbol_names_
. This will create a linker error when the two object files are linked. E.g. g++ -o meta.so meta1.cpp2.o meta2.cpp2.o
. Your solution with CPPFRONT_METAFUNCTION_LIBRARY
will not solve this case, since in both cases you need to call cppfront with CPPFRONT_METAFUNCTION_LIBRARY=meta.so cppfront meta1.cpp2
. This will create the same name for cpp2_metafunction_get_symbol_names_
. If you change the name to CPPFRONT_METAFUNCTION_LIBRARY=meta1.so cppfront meta1.cpp2
then
CPPFRONT_METAFUNCTION_LIBRARIES=meta.so cppfront ...
will not work since the symbol cpp2_metafunction_get_symbol_names_meta_so
is not defined.
There are two options to address this issue:
cpp2_metafunction_get_symbol_names_
is no longer generated automatically. The user has to add it manually. This might be addressed with a metafunction @create_meta_export(func1, func2, func3,...)
. But this would require to have "free flow" meta functions. The code would be:
greeter: (inout t: cpp2::meta::type_declaration) = {
t.add_member($R"(say_hi: () = std::cout << "Hello, world!\nFrom (t.name())$\n";)");
}
@create_meta_export(greeter)
add_metafunction
and then for each metafunction a static registration method is created, e.g.
static const int init_greeter = ::cpp2::meta::add_metafunction(greeter, "greeter");
It think I would prefer option 1.
Here is a patch that removes CPPFRONT_METAFUNCTION_LIBRARY
:
0001-Remove-CPPFRONT_METAFUNCTION_LIBRARY-requirement.txt
On my machine the reduced layout creating and using a metafunction is now:
# Compiling cppfront
g++ -rdynamic cppfront.cpp -o cppfront
# Creating the library
./cppfront metafunctions.cpp2
g++ -std=c++20 -fPIC -shared -o libmetafunctions.so metafunctions.cpp
# Using the library
CPPFRONT_METAFUNCTION_LIBRARIES=./libmetafunctions.so ./cppfront main.cpp2
g++ -std=c++20 main.cpp -o main
./main
I think we should at least attempt to follow the auto-registering mechanism that most test framework out there have (and what I hacked very briefly on my test_metafunction branch), that would be Option 2. In fact, I would go as far as saying that maybe the compiler should have some kind of generic infrastructure to aid this "pattern"? Because this can also be extended not just to test frameworks, but to this metafunction registration/look-up problem, registry of bindings for other languages and probably more. Essentially, you write your code as normal, auto registration is generated on a per-TU basis, and then you need a way to signal the generation of a single and unambiguous function per shared object/program (as opposed to per TU), and we'd want the equivalent as well for tear-down.
EDIT: Note: I completely side-stepped this issue entirely on my test_metafunction branch by piggybacking on the fact that there must be only 1 main
function in the program--I simply inject the "run test framework" in that function. Maybe we could have something similar for metafunctions?
I think we should at least attempt to follow the auto-registering mechanism that most test framework out there have (and what I hacked very briefly on my test_metafunction branch), that would be Option 2. In fact, I would go as far as saying that maybe the compiler should have some kind of generic infrastructure to aid this "pattern"?
Is this similar to what I suggested in the Note in this 797 comment?
Is this similar to what I suggested in the Note in this 797 comment?
If you mean
Future metafunction generation capabilities. We do (eventually) want metafunctions to be able to generate new declarations into existing scopes in the parse tree that are outside the type the metafunction is being applied to
Then yeah, it would be exactly that.
Sorry, I meant this part (I didn't repaste it here because I didn't want to lose the link to Johel's followup comments about feasibility).
(for example, as a strawman: and have the first pass script invoke cppfront with a new flag that says to only compile functions that have a meta API declaration as a parameter; or require user-defined metafunctions to have names that start with @ and have the first pass script invoke cppfront with a new flag that says to only compile functions with a name that starts with @; or something else).
Ah, I think I see what you mean. Let's grok this in 2 parts:
On the double-pass for a single file: I don't think this is can be made full solution currently, as soon as you consider multiple files, everything breaks apart;
Marking metafunctions: I don't think that marking/annotating them is strictly necessary (after all, currently checked-in code simply detects the signature just fine¹), Personally I would argue in favor of actually marking them because:
@test
metafunction stuff. This could solve the problem related to needing CPPFRONT_METAFUNCTION_LIBRARY
, but note that it is not enough to solve the double-pass thing mentioned above, but it does help if later we want to implement something like that.greeter: @meta (inout t: cpp2::meta::type_declaration) = {}
would read as "greeter is a meta function". Which I think sounds proper.
t
and of course lowering with the right DLL visibility.¹ a bit limited but overall seems decent.
Side note: Maybe just me, but its getting harder and harder to keep track of everything that has been discussed, specially since since stuff is getting so meta 😅 I will try to make a post at a later date collecting all the problems and some potential solutions to them in a single post, because this is getting unruly--jumping to several different places to get all the context is hard.
In this post I compile all current "constraints" that I know of, that this specific solution has, as currently implemented in this PR.
Please let me know of any developments so I can directly update this post, and let us enumerate/name these constraints so we can more easily refer to them later.
CPPFRONT_METAFUNCTION_LIBRARY
I have drafted https://github.com/JohelEGP/cppfront/pull/2 in order to possibly tackle Constraint Nº3, Constraint Nº4 and Constraint Nº5.
@DyXel To reply to your email, feel free to take over as you see fit. I haven't been programming lately, and I don't know when I'll be back.
Hey, thanks for your response.
I haven't been programming lately, and I don't know when I'll be back.
I have been there as well, hope to see you active again later!
feel free to take over as you see fit.
Will ponder about it and see where that leads me. Cheers!
I think I have thought about it for long enough, and I've decided to take over this feature; As said in my mail to Johel, my agenda is to simplify this solution by means of re-implementing and/or re-factoring the current work, and to solve several of the constraints mentioned in my comment above while doing so.
Here's my current plan, re-using some (most?) of the work in this PR:
Specifically, I want to go with the route explored in my POC (https://github.com/JohelEGP/cppfront/pull/2), discard the mangling stuff, along other things that might not be necessary.
Rather than having a hardcoded mechanism to detect and register metafunctions, I want to explicitly mark functions with @meta
and be able to emit the out-of-class declaration _ : cpp2::meta::register_function = ("::greeter", std::addressof(::greeter));
, for this I should be able to reuse what its implemented in https://github.com/hsutter/cppfront/pull/809.
In order to tackle Constraint Nº6, I want to give the users the guarantee that their DLLs will be loaded once and unloaded once, per file processed. Which in turn would call static object's constructor and destructors in said DLLs. Later (or in the same bulk if not complicated) we can expose as an API the currently processed unit and such to aid generation.
If there are no objections to this roadmap, I would start working on it next weekend. @MaxSagebaum do I have your permission to reuse your work from https://github.com/hsutter/cppfront/pull/809 if needed?
@DyXel yes sure. You have my permission.
Your plan sound good to me.
feat: evaluate program-defined metafunctions (based on #797)
A metafunction is normal Cpp2 code compiled as part of a library. When parsing a declaration that
@
-uses the metafunction, the library is loaded and the metafunction invoked on the declaration.The reflection API is available by default to Cpp2 code (via
cpp2util.h
). The implementation of the API is provided by thecppfront
executable. For this to work, compilingcppfront
should export its symbols (for an explanation, see https://cmake.org/cmake/help/latest/prop_tgt/ENABLE_EXPORTS.html).For
cppfront
to emit program-defined metafunctions, the environment variableCPPFRONT_METAFUNCTION_LIBRARY
should be set to the library's path.For
cppfront
to load program-defined metafunctions, the environment variableCPPFRONT_METAFUNCTION_LIBRARIES
should be set to the:
-separated library paths of the used metafunctions.Here is an example of program-defined metafunctions. The commands were cleaned up from the CMake buildsystem in #797.
metafunctions.cpp2
:main.cpp2
:Build
cppfront
:Build
metafunctions
:Build and run
main
:Output: