New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Forward declarations limitation in ICE 3.7 #97
Comments
As of Ice 3.7, most Slice forward declarations must be fully defined in the translation unit as described in the release notes. Previously, you could forward declare and never define your interface or class, even if you used it! In C++, you would get a link error when linking your program or shared library if you forgot to provide (and slice-compile) the definition of this forward declared interface or class. While the previous behavior was not documented, we expected that some applications would rely on this behavior and we made this breaking change only because we didn't find a better alternative. With the C++98 mapping, proxies are mapped to our own C++ types, and we could craft them to support this forward-with-no-local-definition Slice feature. However, with the C++11 mapping, proxies are mapped to plain std::shared_ptr, and there is no easy way to support forward-declared only Slice interfaces or classes when the same translation unit also marshals or unmarshals the proxies or class instances. It's also likely new language mappings (such as the new Java mapping or the new MATLAB mapping) rely on this new rule for Slice forward declarations. |
I'm aware that the change is mentioned in the 3.7 release notes. I've also discovered that at least the modern Java mapping fails to be generated if the check for complete definitions is removed. My point is that this limitation is too detrimental, at least in the C++ world, for the reasons I described in the initial post. I'm not familiar with the C++11 mapping yet, but it would seem not that different with regard to forward-declared proxies and classes because you can declare and use shared_ptr to incomplete types just as you could do it with IceUtil::Handle and its cousins. You cannot dereference the pointers, but that should not be required in the generated headers. You don't implement marshalling code in the headers, it is only implemented in the generated source files. One possible solution that I see is to generate forward declaration headers along with the main headers. An
would translate to:
|
The marshaling and unmarshaling in C++11 is actually implemented in part in the header files, which results in less code bloat (only the code for the mapped function you use gets compiled). This also allows us to use template member functions for the future-returning async member functions. But this is optimization is not the main reason behind this change. Take this Slice definition:
slice2cpp generates widget.h and widget.cpp from this definition. Say we don't use any inline code in header files and all the marshaling code is in widget.cpp. How is widget.cpp going to marshal/unmarshal shared_ptr without seeing the full definition for Widget? Note that you can still forward declare all you want interfaces that you don't use in your translation unit. For example, the following is completely valid in 3.7:
Maybe the work-around for your application is to add Slice files with only forward declarations - like widgetF.ice above? And then include widgetF.h in C++ code that doesn't need the full declarations. |
With my suggested solution with the forwarding headers, we would not need to use forward declarations in Slice files as much because the dependency bloat would still be mitigated in the C++ generated code. Other solutions I can think of:
But frankly, the solution with the forwarding headers I like the most so far as it involves least overhead and does not affect the Slice language.
No, that doesn't solve the problem. Basically, what we have now is:
In 3.7, this means that whoever wants to use |
How would your suggested "_fwd.h" solution apply to your sample Slice file?
Unless the generated code uses some C++ trickery (like we did in Ice C++ < 3.7), the generated .cpp file needs to see the full definition of the various Widget interfaces. Now, if this Slice file also includes the definitions for all the Widget1...N interfaces, how would slice2cpp know you want the full WidgetFactoryPrx declaration in widget_fwd.h, but only a forward declaration for the other proxy types? |
As I said, I would not need to use forward declarations in the Slice files and could include the Slice files with full definitions instead.
The generated C++ files would look like this:
As you see, the widgetfactory.h header does not include the definitions of all widgets, only forward declarations. Any users of WidgetFactory will be able to include the widget headers they actually need. widgetfactory.cpp does include full definitions and can contain marshalling code. |
Thanks, I understand your proposal now. It would not change the Slice forward-declaration rule, but it would change the code (and files) generated by slice2cpp. Note that slice2cpp cannot simply generate
So depending on how the Slice types in the included Slice files are used, slice2cpp would have to generate different C++ #includes in This gets even more complicated for indirect inclusions like:
Here, we'd need to generate |
Ok, I see. So we need a Slice extension then, something like |
The fix, as suggested by Lastique in the comment above, was to add a new metadata Without the proper include in the generated source file, this generated source file won't compile. You can find an example in the new test committed by Jose: |
Thanks! Is this only supported in C++11 mapping or in C++03 as well? |
This should work with all language mappings, |
In ICE 3.7 there is a breaking change with regard to forward declarations of interfaces and classes in Slice files. In 3.6, it was possible to forward-declare an interface or class and use its proxy in method parameters, return types, structures and classes. This is no longer possible in 3.7 as the compiler requires the complete definition of the forward-declared interface or class.
This is a severe limitation and a deal breaker in our case. We have used this feature extensively to reduce dependencies between different components, reduce compile times and memory consumption during compilation. Consider a use case with a factory interface that contains methods for creating objects of different types (i.e. having different interfaces). Previously, those interfaces were forward-declared, which allowed users of this factory to only depend on the interfaces they needed. Now, the users have to include all interfaces, including their dependencies, which makes the API pretty much monolithic.
This issue asks to revert the limitation or provide a suitable alternative.
The text was updated successfully, but these errors were encountered: