/Wrapper Generators/ConverterLibrary/ClassTranslator.cpp
C++ | 1962 lines | 1236 code | 314 blank | 412 comment | 298 complexity | 7370052cc25535ff16d7e959c5fb1221 MD5 | raw file
- #include "ClassTranslator.hpp"
- #include "SourceEmitter.hpp"
- #include "RootClassInfo.hpp"
- #include "RootClassInfoCollection.hpp"
- #include "RootClassMethod.hpp"
- #include "CPPNetTypeMapper.hpp"
- #include "RootClassMethodArg.hpp"
- #include "WrapperConfigurationInfo.hpp"
- #include "ROOTHelpers.h"
- #include "ConverterErrorLog.hpp"
- #include "FeatureManager.hpp"
-
- #include "TROOT.h"
- #include "TClass.h"
-
- #include <algorithm>
- #include <sstream>
- #include <fstream>
- #include <vector>
- #include <iostream>
- #include <set>
- #include <stdio.h>
- #include <stdexcept>
-
- using std::ostringstream;
- using std::endl;
- using std::cout;
- using std::ofstream;
- using std::ifstream;
- using std::vector;
- using std::string;
- using std::runtime_error;
- using std::set;
- using std::for_each;
- using std::map;
- using std::pair;
- using std::string;
- using std::getline;
- using std::transform;
- using std::back_inserter;
- using std::inserter;
-
- #ifdef nullptr
- #undef nullptr
- #endif
-
- ClassTranslator::ClassTranslator(const std::string &base_dir)
- : _base_directory (base_dir)
- {
- load_globals();
-
- _default_header_includes.push_back ("NetArrayTranslator.hpp");
- _default_header_includes.push_back ("VectorObject.hpp");
- }
-
- ClassTranslator::~ClassTranslator(void)
- {
- }
-
- /// Drives the translation of a class
- void ClassTranslator::translate(RootClassInfo &class_info)
- {
- ///
- /// Clean up the class inheritance list. And make sure that everything is well in there. If this fails, then
- /// we need fail hard b/c others might be depending on this class being translated.
- ///
-
- clean_inheritance_list (class_info);
- if (!check_inheritance_list(class_info)) {
- throw new runtime_error ("Class '" + class_info.CPPName() + "' has some bad classes in its inherritance tree");
- }
-
- ///
- /// create the template around the outside
- ///
-
- ostringstream cpp_filename, hpp_filename;
- cpp_filename << _base_directory << "\\" << class_info.NETName() << ".cpp";
- hpp_filename << _base_directory << "\\" << class_info.NETName() << ".hpp";
-
- SourceEmitter cpp_emitter (cpp_filename.str());
- SourceEmitter hpp_emitter (hpp_filename.str());
-
- cpp_emitter() << "// Generated by ROOT Wrapper Generator" << endl;
- hpp_emitter() << "// Generated by ROOT Wrapper Generator" << endl;
- hpp_emitter() << "#pragma once" << endl;
- cpp_emitter.include_file(class_info.NETName() + ".hpp");
-
- // HACK Horrible hack: this is because some standalone files require this in order to build (TAttLine, etc.)!
- hpp_emitter.start_line() << "/// Hack to deal with some include files requiring iostream and std to be defined" << endl;
- hpp_emitter.include_file("iostream");
- hpp_emitter.start_line() << "using namespace std;" << endl;
-
- ///
- /// The include file for the ROOT object itself
- ///
-
- hpp_emitter.include_file(class_info.include_filename());
-
- ///
- /// Now, we want every possible method accessible outside the C++ world. The current C++ compiler will make methods
- /// that have raw C++ objects inaccessible outside the library (but accessible within the library). This way, if someone
- /// has to do somethign down and dirty in C++ they can do it without having to hack the code we generate.
- ///
- /// Because multiple objects can be defined in one file, we have to make sure all make_public's are issued for the objects
- /// in that one file. Crazyness. So, we write a seperate include file which all headers include for their header.
- /// Note that we write out the seperate file here. Most files will be written once (since the header files contain
- /// only a single object), but some will be written twice. We can do that or make this a lot more complex! :-)
- ///
-
- hpp_emitter.start_line() << "/// Make sure all native objects are accessible outside the boundries of this library/dll" << endl;
- hpp_emitter.include_file(class_info.LibraryName() + "-make_public.hpp");
- _objects_in_library[class_info.LibraryName()].insert(class_info.CPPName());
- _base_directories[class_info.LibraryName()] = _base_directory;
-
- ///
- /// If we have a non-TObject, then we will be needing TClass...
- ///
-
- if (!class_info.InheritsFromTObject() && class_info.CPPName() != "TObject")
- {
- hpp_emitter.include_file("TClass.h");
- }
-
- ///
- /// And the include files for all the interfaces that we will be referencing.
- /// Mark these guys as a dependencies as well, as we will need them in order to
- /// build!
- ///
- /// Note that the includes between libraries aren't strictly needed. That is -- if we are building these objects in
- /// different libraries then it really doesn't matter if we do the include (it does matter within the same library,
- /// however). But we treat all objects as the same -- and go overboard with the includes -- because that makes this
- /// bit of code cleaner, and (I hope) doen't slow down the resulting build very much.
- ///
-
- for (unsigned int i = 0; i < class_info.GetDirectInheritedClasses().size(); i++) {
- RootClassInfo &dep_class(RootClassInfoCollection::GetRootClassInfo(class_info.GetDirectInheritedClasses()[i]));
- if (emit_this_header(class_info, dep_class)) {
- hpp_emitter.include_file(dep_class.NETName() + ".hpp");
- }
- if (class_info.LibraryName() != dep_class.LibraryName()) {
- _library_dependencies[class_info.LibraryName()].insert(dep_class.LibraryName());
- }
- }
-
- set<string> referenced_classes;
- copy (class_info.GetReferencedClasses().begin(), class_info.GetReferencedClasses().end(),
- inserter(referenced_classes, referenced_classes.begin()));
- auto featureClasses = FeatureManager::GetFeaturesFor(class_info).get_additional_root_class_references(class_info);
- copy (featureClasses.begin(), featureClasses.end(), inserter(referenced_classes, referenced_classes.begin()));
- vector<string> v_referenced_classes (referenced_classes.begin(), referenced_classes.end());
-
- for (unsigned int i = 0; i < v_referenced_classes.size(); i++) {
- RootClassInfo &dep_class(RootClassInfoCollection::GetRootClassInfo(v_referenced_classes[i]));
- if (CPPNetTypeMapper::instance()->has_mapping(dep_class.CPPName())) {
- if (emit_this_header(class_info, dep_class)) {
- cpp_emitter.include_file(dep_class.NETName() + ".hpp");
- } else {
- cpp_emitter.include_file(dep_class.include_filename());
- }
- if (class_info.LibraryName() != dep_class.LibraryName()) {
- _library_dependencies[class_info.LibraryName()].insert(dep_class.LibraryName());
- }
- }
- }
-
- for (unsigned int i = 0; i < class_info.GetReferencedEnums().size(); i++) {
- RootEnum dep_enum (class_info.GetReferencedEnums()[i]);
- if (CPPNetTypeMapper::instance()->has_mapping(dep_enum.NameQualified())) {
- if (!emit_this_enum(class_info, dep_enum)) {
- cpp_emitter.include_file(dep_enum.include_filename());
- }
- }
- }
-
- ///
- /// Always enable the referencing of arrays
- ///
-
- emit_hpp_headers(hpp_emitter);
-
- ///
- /// We use exceptions...
- ///
-
- cpp_emitter.include_file("stdexcept");
-
- ///
- /// Enum's can't be forward declared -- they have to be included... If they are class enums, however, then
- /// they should just come in. If they are in other libraries, then they will be pulled in with the reference.
- /// The trick comes b/c if they are defined in the library we are working on right now, then they will have to
- /// be included. Ugh.
- ///
-
- for (unsigned int i = 0; i < class_info.GetReferencedEnums().size(); i++) {
- RootEnum dep_enum (class_info.GetReferencedEnums()[i]);
- if (CPPNetTypeMapper::instance()->has_mapping(dep_enum.NameQualified())) {
- if (!dep_enum.IsClassDefined()) {
- if (emit_this_enum(class_info, dep_enum)) {
- hpp_emitter.include_file(dep_enum.NameUnqualified() + ".hpp");
- }
- } else {
- if (dep_enum.LibraryName() == class_info.LibraryName()) {
- if (dep_enum.NETClassName() != class_info.NETName()) {
- hpp_emitter.include_file(dep_enum.NETClassName() + ".hpp");
- }
- }
- }
- }
- }
-
- ///
- /// Put everything in a ROOT namespace to keep it clean...
- ///
-
- hpp_emitter.start_namespace ("ROOTNET");
- cpp_emitter.start_namespace ("ROOTNET");
-
- ///
- /// Forward declare the various .net classes we might be referencing in the
- /// header...
- ///
-
- for (unsigned int i = 0; i < class_info.GetReferencedClasses().size(); i++) {
- string referenced_classname = class_info.GetReferencedClasses()[i];
- if (CPPNetTypeMapper::instance()->has_mapping(referenced_classname)) {
- hpp_emitter.forward_class_reference(RootClassInfoCollection::GetRootClassInfo(referenced_classname).NETName());
- }
- }
-
-
- ///
- /// Generate the interface and the actual class
- ///
-
- generate_interface (class_info, hpp_emitter);
- generate_class_methods (class_info, cpp_emitter);
- generate_interface_static_methods (class_info, cpp_emitter);
- generate_class_header (class_info, hpp_emitter);
-
- ///
- /// Now the full on object.
- ///
-
- hpp_emitter.brace_close();
- cpp_emitter.brace_close();
-
- ///
- /// Close everything up so we can get on with things
- ///
-
- hpp_emitter.close();
- cpp_emitter.close();
- }
-
- void ClassTranslator::emit_hpp_headers(SourceEmitter &hpp_emitter)
- {
- for (int i = 0; i < _default_header_includes.size(); i++) {
- hpp_emitter.include_file(_default_header_includes[i]);
- }
- }
-
-
- /// Helper class to dump out the make public files.
- class write_out_make_public
- {
- public:
- inline write_out_make_public (const map<string, string> base_directories)
- : _base_directory(base_directories)
- {
- }
- void operator() (const pair<string, set<string> > &item);
- private:
- const map<string, string> _base_directory;
- };
-
- ///
- /// Write out all the make-public statements and forward declare all the classes. We do this to uniformly expose all the
- /// various items to other libraries.
- ///
- void ClassTranslator::finalize_make_publics()
- {
- for_each (_objects_in_library.begin(), _objects_in_library.end(),
- write_out_make_public(_base_directories));
- }
- void write_out_make_public::operator ()(const std::pair<string,set<string> > &item)
- {
- string library_name (item.first);
- set<string> all_objects_set (item.second);
-
- ostringstream make_public_filename;
- map<string, string>::const_iterator dir_loc = _base_directory.find(library_name);
- if (dir_loc == _base_directory.end()) {
- throw runtime_error ("Unable to find base directory for library " + library_name);
- }
- make_public_filename << dir_loc->second << "\\" << library_name << "-make_public.hpp";
-
- SourceEmitter make_public_header (make_public_filename.str());
- make_public_header.start_line() << "#pragma once" << endl;
- make_public_header.start_line() << "// List of the objects defined in this library, tagged with make_public" << endl;
- vector<string> all_objects(all_objects_set.begin(), all_objects_set.end());
- for (unsigned int i = 0; i < all_objects.size(); i++) {
- make_public_header.start_line() << "class " << all_objects[i] << ";" << endl;
- make_public_header.start_line() << "#pragma make_public(" << all_objects[i] << ")" << endl;
- }
- make_public_header.close();
- }
-
- namespace {
- /// Indexers -- using the [] operators in C++ -- are a tried and true way of getting and setting
- /// elements inside an array like object. .NET supports this in the form of "indexers" -- but
- /// they require a "get" and a "set" method, and you don't use a reference. So we have to sort
- /// through the C++ code and see if we can tease all that information out.
-
- /// Store info about the operator[] pairs that are out there.
- class CPPIndexerInfo
- {
- public:
- /// Translators to represent the C++/.NET arguments.
- const CPPNetTypeMapper::TypeTranslator *_index_type;
- const CPPNetTypeMapper::TypeTranslator * _return_type;
-
- /// The method this is based on
- const RootClassMethod *_method;
-
- /// True if this has a get and a set (you can set the internal value). You can always get it...
- bool _is_setter;
-
- inline CPPIndexerInfo (void)
- : _is_setter(false), _index_type(0), _return_type(0)
- {}
-
- };
-
- /// Helper task to convert a map into a vector. Just makes code below
- /// prettier.
- template<class C>
- class copy_map_target_t
- {
- public:
- template<class I>
- inline void operator() (const pair<I,C> &item)
- {
- _vector.push_back(item.second);
- }
- inline const vector<C> &as_vector(void) const
- {
- return _vector;
- }
- private:
- vector<C> _vector;
- };
-
- /// Given a list of indexers, try to find all the ones that match up.
- vector<CPPIndexerInfo> SortIndexers(const vector<RootClassMethod> &index_list)
- {
- map<string, CPPIndexerInfo> result_map;
-
- for (int i = 0; i < index_list.size(); i++) {
- const RootClassMethod &method(index_list[i]);
-
- /// Figure out what this guy is
- string return_type = method.return_type();
- if (method.arguments().size() != 1) {
- ConverterErrorLog::log_type_error(method.ClassOfMethodDefinition(),
- "Indexer with return type " + return_type + " does not have a single argument!");
- continue;
- }
- const string index_argument = method.arguments()[0].NETInterfaceTypeName();
-
- /// Look for clues in the return type to see what the ROOT Dev team wanted to do with this.
- bool has_const = false;
- bool has_reference = false;
- bool is_abs_class = false;
-
- if (return_type.find("const ") == 0) {
- has_const = true;
- }
- int ref_index = return_type.find("&");
- if (ref_index != return_type.npos) {
- //return_type = return_type.substr(0,ref_index) + return_type.substr(ref_index+1);
- has_reference = true;
- }
-
- const CPPNetTypeMapper::TypeTranslator *tt = CPPNetTypeMapper::instance()->get_translator_from_cpp(return_type);
-
- TClass *return_type_class = gROOT->GetClass(tt->cpp_core_typename().c_str());
- if (return_type_class != 0) {
- is_abs_class = (return_type_class->Property() & kIsAbstract) != 0;
- }
-
- /// Given that, is this a getter or a setter? If the return is const, then this
- /// is a getter, no matter if it is a reference. Otherwise, a reference operator means
- /// it was a setter.
- bool is_setter = has_reference && !has_const && !is_abs_class;
-
- /// Now, lets log this puppy in. Error if return types are different!
- if (result_map.find(index_argument) == result_map.end()) {
- CPPIndexerInfo temp;
- temp._index_type = CPPNetTypeMapper::instance()->get_translator_from_cpp(method.arguments()[0].CPPTypeName());
- temp._return_type = tt;
- temp._method = &method;
- result_map[index_argument] = temp;
- } else {
- if (result_map[index_argument]._return_type->cpp_core_typename() != tt->cpp_core_typename()) {
- is_setter = false;
- ConverterErrorLog::log_type_error(method.ClassOfMethodDefinition(), "Indexer with argument '" + index_argument + "' has more than one return type ('"
- + return_type + "', '" + result_map[index_argument]._return_type->cpp_typename() + "')");
- }
- }
- if (is_setter) {
- result_map[index_argument]._is_setter = true;
- }
- }
-
- /// Turn it into a vector to return
-
- vector<CPPIndexerInfo> result = for_each(result_map.begin(), result_map.end(),
- copy_map_target_t<CPPIndexerInfo>()).as_vector();
-
- return result;
- }
-
- ///
- /// Returns a list of deep non-inherrited classes.
- ///
- set<string> GetNonInherritedClasses(const RootClassInfo &info)
- {
- auto &superClasses (info.GetDirectInheritedClasses());
- auto directSuper (info.GetBestClassToInherrit());
- set<string> inh_classes;
- for_each(superClasses.begin(), superClasses.end(), [&] (const string &cname)
- {
- if (directSuper != cname) {
- auto &classInfo (RootClassInfoCollection::GetRootClassInfo(cname));
- auto &deepClasses (classInfo.GetInheritedClassesDeep());
- copy(deepClasses.begin(), deepClasses.end(), inserter(inh_classes, inh_classes.begin()));
- inh_classes.insert(cname);
- }
- });
- inh_classes.insert(info.CPPName());
-
- return inh_classes;
- }
-
- // See if this property should be emitted in the class that is being worked on. The
- // list of classes that aren't in the direct inherritance path is passed to figure this out.
- bool propertyShouldBeEmitted (const RootClassProperty &prop, const set<string> &nonInheritedClasses)
- {
- bool getter_good = prop.isGetter() && nonInheritedClasses.find(prop.getter_method()->ClassOfMethodDefinition()) != nonInheritedClasses.end();
- bool setter_good = prop.isSetter() && nonInheritedClasses.find(prop.setter_method()->ClassOfMethodDefinition()) != nonInheritedClasses.end();
-
- return getter_good || setter_good;
- }
- }
-
- class emit_enum_as_static {
- public:
- inline emit_enum_as_static (SourceEmitter &emitter)
- : _emitter(emitter)
- {}
- inline void operator() (const pair<string, unsigned int> &item)
- {
- _emitter.start_line() << "static const unsigned int " << item.first << " = " << item.second << ";" << endl;
- }
- private:
- SourceEmitter &_emitter;
- };
-
- ///
- /// Helper functiont to write out static enum defs
- ///
- void write_header_enum_standard (const RootEnum &einfo, SourceEmitter &emitter, bool is_global = false)
- {
- if (is_global) {
- emitter.start_line() << "public ";
- } else {
- emitter.start_line();
- }
- emitter() << "enum class ";
- if (einfo.NameUnqualified() == "") {
- emitter() << "EClassConstants";
- } else {
- emitter() << einfo.NameUnqualified();
- }
- emitter() << endl;
-
- emitter.brace_open();
- for (int en = 0; en < einfo.values().size(); en++) {
- emitter.start_line() << einfo.values()[en].first << " = " << einfo.values()[en].second;
- if (en != einfo.values().size()-1) {
- emitter() << ",";
- }
- emitter() << endl;
- }
- emitter.brace_close(true);
- }
-
- ///
- /// Write the enum specs
- ///
- void write_header_enums (RootClassInfo &class_info, SourceEmitter &emitter, bool all_standard_format)
- {
- const vector<RootEnum> &class_enums (class_info.GetClassEnums());
- for (unsigned int i = 0; i < class_enums.size(); i++) {
- /// If they are not a named enum, then simulate them with a set of constants
- if (class_enums[i].NameUnqualified() == "" && !all_standard_format) {
- std::for_each (class_enums[i].values().begin(), class_enums[i].values().end(),
- emit_enum_as_static (emitter));
- } else {
- write_header_enum_standard (class_enums[i], emitter);
- }
- }
-
- }
-
- ///
- /// Generate the interface specification
- ///
- void ClassTranslator::generate_interface (RootClassInfo &class_info, SourceEmitter &emitter)
- {
-
- emitter.start_namespace("Interface");
-
- ///
- /// Add forward references for the interfaces
- ///
-
- for (unsigned int i = 0; i < class_info.GetReferencedClasses().size(); i++) {
- string referenced_classname = class_info.GetReferencedClasses()[i];
- if (CPPNetTypeMapper::instance()->has_mapping(referenced_classname)) {
- emitter.forward_interface_reference(RootClassInfoCollection::GetRootClassInfo(referenced_classname).NETName());
- }
- }
-
- ///
- /// And now the interface itself.
- ///
-
- auto class_features = FeatureManager::GetFeaturesFor(class_info);
-
- emitter.start_line() << "public interface class " << class_info.NETName() << endl;
-
- ///
- /// The interface can inherrit from a number of places...
- ///
-
- const vector<string> &inherited_classes (class_info.GetDirectInheritedClasses());
- set<string> inherited_interfaces;
- transform (inherited_classes.begin(), inherited_classes.end(),
- inserter(inherited_interfaces, inherited_interfaces.begin()),
- [] (const string &s) { return "ROOTNET::Interface::" + RootClassInfoCollection::GetRootClassInfo(s).NETName(); });
- auto others = class_features.get_additional_interfaces(class_info);
- copy (others.begin(), others.end(), inserter(inherited_interfaces, inherited_interfaces.begin()));
-
- if (inherited_interfaces.size() > 0) {
- emitter.start_line() << " : ";
- bool one = false;
- for_each (inherited_interfaces.begin(), inherited_interfaces.end(),
- [&emitter, &one] (const string &s) {
- if (one)
- emitter() << ", ";
- one = true;
- emitter() << s;
- });
-
- emitter() << endl;
- }
-
- emitter.brace_open();
-
- ///
- /// The special prototype for accessing the raw objects...
- ///
-
- emitter.start_line() << "::" << class_info.CPPName() << " *CPP_Instance_" << class_info.CPPName() << "(void);" << endl;
-
- ///
- /// Special prototype to set-to-null the instance object when ROOT (or similar) makes it go away, or to
- /// delete it.
- ///
-
- emitter.start_line() << "void SetNull (void);" << endl;
- emitter.start_line() << "void DeleteHeldObject (void);" << endl;
- emitter.start_line() << "void DropObjectFromTables (void);" << endl;
- emitter.start_line() << "int GetRawCPPPointer(void);" << endl;
-
- ///
- /// Put in all class defined enums now. They have to go early b/c they have to be declared
- /// when they are defined.
- ///
-
- write_header_enums (class_info, emitter, true);
-
- ///
- /// List all the prototypes for the class.
- /// TODO: This comes back clean, and needs to be fixed so that the fact we couldn't do a bunch of
- /// prototypes was because they weren't translatable needs to get recoreded! :-)
- ///
-
- set<string> already_done_headers;
- vector<RootClassMethod> indexerOperators;
- auto inh_classes(GetNonInherritedClasses(class_info));
-
- const vector<RootClassMethod> class_protos (class_info.GetAllPrototypesForThisClass(true));
- for (unsigned int i = 0; i < class_protos.size(); i++) {
- const RootClassMethod &method = class_protos[i];
-
- // Make sure what we are looking at should be in here!
- if (inh_classes.find(method.ClassOfMethodDefinition()) == inh_classes.end())
- continue;
-
- /// If it is an indexer, keep it for later
- if (method.IsIndexer()) {
- indexerOperators.push_back(method);
- }
-
- if (!method.IsGoodForInterface()) {
- continue;
- }
-
- /// Generate and write out the header.
-
- try {
- if (!method.has_return_value() || !method.get_return_type_translator()->is_reference_to_object()) {
- string n_header (method.generate_normalized_method_header());
-
- if (already_done_headers.find(n_header) == already_done_headers.end()) {
- already_done_headers.insert(n_header);
- if (method.IsStatic()) {
- emitter.start_line() << "static ";
- }
- emitter.start_line() << method.generate_method_header() << ";" << endl;
- }
- }
- } catch (runtime_error &e)
- {
- cout << " interface translation failed (" << method.CPPName() << "): " << e.what() << endl;
- }
- }
-
- ///
- /// Do the properties for this object.
- ///
-
- const vector<RootClassProperty> &properties (class_info.GetProperties());
- for (vector<RootClassProperty>::const_iterator itr = properties.begin(); itr != properties.end(); itr++) {
- // Make sure at least one of these guys is defined in this thing
- if (!propertyShouldBeEmitted(*itr, inh_classes))
- continue;
-
- emitter.start_line();
- if (itr->isStatic())
- emitter() << "static ";
- emitter() << "property " << itr->property_type() << " " << itr->name() << " {" << endl;
- if (itr->isGetter()) {
- emitter.start_line() << " " << itr->property_type() << " get ();" << endl;
- }
- if (itr->isSetter()) {
- emitter.start_line() << " void set (" << itr->property_type() << " value);" << endl;
- }
- emitter.start_line() << "}" << endl;
- }
-
- ///
- /// Do the fields for this object
- ///
-
- auto &fields (class_info.GetAllDataFields(true));
- for (int i = 0; i < fields.size(); i++) {
- const RootClassField &f(fields[i]);
- emitter.start_line() << "property " << f.NETType() << " " << f.NETName() << " {" << endl;
- if (f.GetterOK()) {
- emitter.start_line() << " " << f.NETType() << " get ();" << endl;
- }
- if (f.SetterOK()) {
- emitter.start_line() << " void set (" << f.NETType() << " value);" << endl;
- }
- emitter.start_line() << "}" << endl;
- }
-
- ///
- /// Put in a reference to the indexers so others can do the array lookups. :-)
- ///
-
- vector<CPPIndexerInfo> indexers (SortIndexers(indexerOperators));
- for (unsigned int i = 0; i < indexers.size(); i++) {
-
- CPPIndexerInfo &info (indexers[i]);
- if (inh_classes.find(info._method->ClassOfMethodDefinition()) == inh_classes.end())
- continue;
-
- emitter.start_line() << "property " << info._return_type->net_return_type_name() << " default[" << info._index_type->net_interface_name() << "] {" << endl;
- emitter.start_line() << " " << info._return_type->net_return_type_name() << " get (" << info._index_type->net_interface_name() << " index);" << endl;
- if (info._is_setter) {
- emitter.start_line() << " void set (" << info._index_type->net_interface_name() << " index, " << info._return_type->net_return_type_name() << " value);" << endl;
- }
- emitter.start_line() << "}" << endl;
- }
-
- ///
- /// Great. Now we can close it out
- ///
-
- emitter.brace_close(true); // End of class definition
- emitter.brace_close(); // End of namespace
- }
-
- ///
- /// Generate the interface static methods.
- ///
- void ClassTranslator::generate_interface_static_methods (RootClassInfo &class_info, SourceEmitter &emitter)
- {
-
- emitter.start_namespace("Interface");
-
- ///
- /// Work only on static methods here...
- ///
-
- auto inh_classes(GetNonInherritedClasses(class_info));
- set<string> already_done_headers;
- const vector<RootClassMethod> &protos (class_info.GetAllPrototypesForThisClass(true));
- for (unsigned int i = 0; i < protos.size(); i++) {
- const RootClassMethod &method = protos[i];
- if (!method.IsStatic() || !method.IsGoodForInterface()) {
- continue;
- }
- if (inh_classes.find(method.ClassOfMethodDefinition()) == inh_classes.end())
- continue;
-
- /// Generate and write out the header.
-
- try {
- string n_header (method.generate_normalized_method_header());
-
- if (already_done_headers.find(n_header) == already_done_headers.end()) {
- already_done_headers.insert(n_header);
- emitter.start_line() << method.generate_method_header(true) << "" << endl;
- } else {
- continue;
- }
- } catch (runtime_error &e)
- {
- cout << " interface translation failed (" << method.CPPName() << "): " << e.what() << endl;
- }
-
- ///
- /// Generate a call to the static method of the actual object!
- ///
-
- emitter.brace_open();
-
- emitter.start_line();
- if (method.has_return_value()) {
- emitter() << "return ";
- }
- emitter() << "ROOTNET::" << class_info.NETName() << "::" << method.NETName() << "(";
- for (unsigned int i = 0; i < method.arguments().size(); i++) {
- if (i > 0) {
- emitter() << ", ";
- }
- emitter() << method.arguments()[i].get_argname();
- }
- emitter() << ");" << endl;
- emitter.brace_close();
- }
-
- ///
- /// Next, do the properties for the interface that have been declared static.
- ///
-
- const vector<RootClassProperty> &properties (class_info.GetProperties());
- for (vector<RootClassProperty>::const_iterator itr = properties.begin(); itr != properties.end(); itr++) {
- if (!itr->isStatic())
- continue;
-
- // Make sure at least one of these guys is defined in this thing
- if (!propertyShouldBeEmitted(*itr, inh_classes))
- continue;
-
- if (itr->isGetter()) {
- emitter.start_line() << itr->property_type() << " " << class_info.NETName() << "::" << itr->name() << "::get ()" << endl;
- emitter.brace_open();
- emit_function_body(*(itr->getter_method()), class_info, emitter);
- emitter.brace_close();
- }
- if (itr->isSetter()) {
- emitter.start_line() << "void " << class_info.NETName() << "::" << itr->name() << "::set (" << itr->property_type()
- << " " << itr->setter_method()->arguments()[0].get_argname() << ")" << endl;
- emitter.brace_open();
- emit_function_body(*(itr->setter_method()), class_info, emitter);
- emitter.brace_close();
- }
- }
-
-
- ///
- /// Great. Now we can close it out
- ///
-
- emitter.brace_close(); // End of namespace
- }
-
- namespace {
- // Emit the method headers for an operator. This is different than the classic one because
- // it is static - so we have to have both guys as operators.
- // We reverse the order so that as long as their is one
- vector<string> generate_operator_header (const RootClassMethod &method, bool emit_class_method_name = false)
- {
- auto mainObj = CPPNetTypeMapper::instance()->get_translator_from_cpp(method.OwnerClass().CPPName());
- auto base_arg (mainObj->net_typename() + " base_obj_a1");
-
- string args (method.generate_normalized_method_arguments(true));
- string args_sep = ", ";
- if (args.length() == 0)
- args_sep = "";
-
- //
- // We reverse the arguments so that you can something like "5 * obj" and "obj * 5".
- // However, if it is "obj * obj" and both obj's are same, then we can't do this because
- // we'll end up with ambigous operators.
- //
-
- int max_count = 2;
- const RootClassInfo &cInfo (method.OwnerClass());
- if (method.arguments().size() == 0)
- max_count = 1;
- if (method.arguments().size() > 0 && method.arguments()[0].RawCPPTypeName() == cInfo.CPPName())
- max_count = 1;
-
- //
- // Emit everything
- //
-
- vector<string> result;
- for (int count = 0; count < max_count; count++) {
- ostringstream header;
- if (method.has_return_value()) {
- header << method.get_return_type_translator()->net_typename() << " ";
- } else {
- header << "void ";
- }
-
- if (emit_class_method_name) {
- header << method.OwnerClass().NETName() << "::";
- }
-
- header << method.NETName()
- << "(";
-
- if (count == 0) {
- header << base_arg << args_sep << args;
- } else {
- header << args << args_sep << base_arg;
- }
- header << ")";
- result.push_back(header.str());
- }
- return result;
- }
- }
-
- ///
- /// Generate the header for the class.
- ///
- void ClassTranslator::generate_class_header (RootClassInfo &info, SourceEmitter &emitter)
- {
- ///
- /// Emit the class declaration, along with the inherritance path.
- ///
-
- emitter.start_line() << "public ref class " << info.NETName() << endl;
-
- auto bestClassToInherrit (info.GetBestClassToInherrit());
- if (bestClassToInherrit.size() == 0) {
- emitter.start_line() << " : ROOTNET::Utility::ROOTDOTNETBaseTObject," << endl;
- } else {
- auto &infoInherrit (RootClassInfoCollection::GetRootClassInfo(bestClassToInherrit));
- emitter.start_line() << " : ROOTNET::" << infoInherrit.NETName() << "," << endl;
- }
- emitter.start_line() << " ROOTNET::Interface::" << info.NETName() << endl;
- emitter.brace_open();
-
- //
- // Do protected
- //
-
- emitter.start_line() << "protected:" << endl;
- emitter.start_line() << " void SetInstance (::" << info.CPPName() << "* instance) {" << endl;
- emitter.start_line() << " _instance = instance;" << endl;
- if (bestClassToInherrit.size() != 0)
- emitter.start_line() << " N" << bestClassToInherrit << "::SetInstance (instance);" << endl;
- emitter.start_line() << "}" << endl;
-
- ///
- /// Hold onto the C++ pointer!
- ///
-
- emitter.start_line() << "private:" << endl;
- emitter.start_line() << "::" << info.CPPName() << " *_instance;" << endl;
- emitter() << endl;
-
- ///
- /// Give us some access to the C++ pointers for all inherited classes...
- /// When dealing with inherritance make sure that we do the CPP_Instance's
- /// correctly.
- ///
-
- auto inh_classes(GetNonInherritedClasses(info));
- for_each(inh_classes.begin(), inh_classes.end(), [&] (const string &cls)
- {
- if (cls.find("<") == cls.npos) {
- emitter.start_line() << "public:" << endl;
- emitter.start_line() << "virtual ::" << cls << " *CPP_Instance_" << cls << "(void)" << endl;
- emitter.brace_open();
- emitter.start_line() << " return _instance;" << endl;
- emitter.brace_close();
- }
- });
-
- ///
- /// And a ctor that can start from a pointer
- ///
-
- emitter.start_line() << info.NETName() << "(::" << info.CPPName() << " *instance);" << endl;
-
- emitter.start_line() << info.NETName() << "(::" << info.CPPName() << " &instance);" << endl;
-
- //
- // Common method to get out the TObject and void pointer.
- //
-
- emitter.start_line() << "protected:" << endl;
- if (info.InheritsFromTObject() || info.CPPName() == "TObject") {
- emitter.start_line() << "virtual ::TObject *GetTObjectPointer (void) override { return _instance; }" << endl;
- emitter.start_line() << "virtual ::TClass *GetAssociatedTClassInfo (void) override" << endl;
- emitter.brace_open();
- emitter.start_line() << "if (_instance == nullptr)" << endl;
- emitter.start_line() << " return nullptr;" << endl;
- emitter.start_line() << "return _instance->IsA();" << endl;
- emitter.brace_close();
- } else {
- emitter.start_line() << "virtual ::TObject *GetTObjectPointer (void) override { return (::TObject*) 0; }" << endl;
- emitter.start_line() << "virtual ::TClass *GetAssociatedTClassInfo (void) override" << endl;
- emitter.brace_open();
- emitter.start_line() << "return TClass::GetClass(\"" << info.CPPName() << "\");" << endl;
- emitter.brace_close();
- }
- emitter.start_line() << "virtual void *GetVoidPointer (void) override { return _instance; }" << endl;
-
- ///
- /// And a guy to set the thing to null
- ///
-
- emitter.start_line() << "public:" << endl;
- emitter.start_line() << "virtual void SetNull (void) override { _instance = 0; SetNullReason(ROOTNET::Utility::ReasonPointerNullEnum::kSetNullCalled);}" << endl;
- emitter.start_line() << "virtual void DropObjectFromTables (void) override;" << endl;
-
- ///
- /// Allow for deleting, as long as we can get at the dtor!
- ///
-
- if (info.CanDelete()) {
- emitter.start_line() << "virtual void DeleteHeldObject (void) override { delete _instance; _instance = 0;SetNullReason(ROOTNET::Utility::ReasonPointerNullEnum::kObjectDeleted);}" << endl;
- } else {
- emitter.start_line() << "virtual void DeleteHeldObject (void) override {_instance = 0;SetNullReason(ROOTNET::Utility::ReasonPointerNullEnum::kObjectNotDeleted);}" << endl;
- }
-
- ///
- /// Allow one to get a hold of the raw ponter. Yes, this is evil. But sometimes it is necessary.
- /// This will break if built in x64 I would guess. :-(
- ///
-
- emitter.start_line() << "virtual int GetRawCPPPointer(void)";
- if (bestClassToInherrit.size() > 0)
- emitter() << " new";
- emitter() << " {return (int) _instance;}" << endl;
-
- ///
- /// Emit all the method signatures...
- ///
-
- set<string> written_methods;
- vector<RootClassMethod> arrayOperators;
- vector<RootClassMethod> mathOperators;
- const vector<RootClassMethod> &class_protos(info.GetAllPrototypesForThisClass(true));
- for (unsigned int i = 0; i < class_protos.size(); i++) {
- const RootClassMethod &method = class_protos[i];
-
- //
- // We only want to implement this class if it is in one of the classes we have implemented as an
- // interface.
- //
-
- if (inh_classes.find(method.ClassOfMethodDefinition()) == inh_classes.end())
- continue;
-
- //
- // Accumulate the various indexers, or if there is a problem with this method, etc.
- //
-
- if (method.IsIndexer()) {
- arrayOperators.push_back(method);
- continue;
- }
- if (!method.IsGoodForClass()) {
- continue;
- }
-
- if (method.IsMathOperator())
- {
- mathOperators.push_back(method);
- continue;
- }
-
- try {
- if (method.IsCtor()) {
- string n_header = method.generate_normalized_method_header();
- if (written_methods.find(n_header) != written_methods.end()) {
- continue;
- }
- written_methods.insert(n_header);
-
- emitter.start_line() << method.generate_method_header() << ";" << endl;
- } else {
- /// We don't deal with references in returns yet...
- if (!method.has_return_value() || !method.get_return_type_translator()->is_reference_to_object()) {
- string n_header = method.generate_normalized_method_header();
- if (written_methods.find(n_header) != written_methods.end()) {
- ConverterErrorLog::log_type_error(method.return_type(), "Don't do references as returns from methods yet");
- continue;
- }
- written_methods.insert(n_header);
-
- ///
- /// The header should have everything in it, and since it is implementing something in
- /// the interface, we had better mark it virtual. Unless it is static, in which case it only
- /// exists here.
- ///
-
- emitter.start_line();
- if (method.IsStatic()) {
- emitter() << "static ";
- } else {
- emitter() << "virtual ";
- }
- emitter() << method.generate_method_header();
- if (method.IsDefaultOverride())
- emitter() << " override";
- emitter() << ";" << endl;
- }
- }
- } catch (runtime_error &e) {
- cout << " translation failed (" << method.CPPName() << "): " << e.what() << endl;
- continue;
- }
- }
-
- //
- // Do the math operators. Here we need to generate static versions so C# knows what to do.
- //
-
- emitter.start_line() << "// Math Operators for C#-like languages" << endl;
- for (vector<RootClassMethod>::const_iterator itr = mathOperators.begin(); itr != mathOperators.end(); itr++) {
- auto headers (generate_operator_header (*itr));
- for (vector<string>::const_iterator itr_h = headers.begin(); itr_h != headers.end(); itr_h++) {
- emitter.start_line() << "static ";
- emitter() << *itr_h << ";" << endl;
- }
- }
-
- ///
- /// Do the properties for this object.
- ///
-
- const vector<RootClassProperty> &properties (info.GetProperties());
- for (vector<RootClassProperty>::const_iterator itr = properties.begin(); itr != properties.end(); itr++) {
-
- // Make sure at least one of these guys is defined in this thing
- if (!propertyShouldBeEmitted(*itr, inh_classes))
- continue;
-
- // Emit the proper getters and setters
-
- emitter.start_line();
- if (itr->isStatic())
- emitter() << "static ";
- emitter() << "property " << itr->property_type() << " " << itr->name() << " {" << endl;
-
- if (itr->isGetter()) {
- emitter.start_line() << " ";
- if (!itr->isStatic())
- emitter() << "virtual ";
- emitter() << itr->property_type() << " get ()";
- if (itr->getter_method()->IsDefaultOverride())
- emitter() << " new";
- emitter() << ";" << endl;
- }
-
- if (itr->isSetter()) {
- emitter.start_line() << " ";
- if (!itr->isStatic())
- emitter() << "virtual ";
- emitter() << "void set (" << itr->property_type() << " value)";
- if (itr->setter_method()->IsDefaultOverride())
- emitter() << " new";
- emitter() << ";" << endl;
- }
- emitter.start_line() << "}" << endl;
- }
-
- ///
- /// Do the fields for this object
- ///
-
- auto &fields (info.GetAllDataFields(true));
- for (int i = 0; i < fields.size(); i++) {
- const RootClassField &f(fields[i]);
-
- // Make sure this field is in the proper class!
- if (inh_classes.find(f.ClassOfFieldDefinition()) == inh_classes.end())
- continue;
-
- emitter.start_line() << "property " << f.NETType() << " " << f.NETName() << " {" << endl;
- if (f.GetterOK()) {
- emitter.start_line() << " virtual " << f.NETType() << " get ();" << endl;
- }
- if (f.SetterOK()) {
- emitter.start_line() << " virtual void set (" << f.NETType() << " value);" << endl;
- }
- emitter.start_line() << "}" << endl;
- }
-
- ///
- /// Next, put in a reference to the indexers so others can do the array lookups. :-)
- ///
-
- vector<CPPIndexerInfo> indexers (SortIndexers(arrayOperators));
- for (unsigned int i = 0; i < indexers.size(); i++) {
- CPPIndexerInfo &info (indexers[i]);
-
- if (inh_classes.find(info._method->ClassOfMethodDefinition()) == inh_classes.end())
- continue;
-
- emitter.start_line() << "property " << info._return_type->net_return_type_name() << " default[" << info._index_type->net_interface_name() << "] {" << endl;
- emitter.start_line() << " virtual " << info._return_type->net_return_type_name() << " get (" << info._index_type->net_interface_name() << " index)";
- if (info._method->IsDefaultOverride())
- emitter() << " override";
- emitter() << ";" << endl;
-
- if (info._is_setter) {
- emitter.start_line() << " virtual void set (" << info._index_type->net_interface_name() << " index, " << info._return_type->net_return_type_name() << " value)";
- if (info._method->IsDefaultOverride())
- emitter() << " override";
- emitter() << ";" << endl;
- }
- emitter.start_line() << "}" << endl;
- }
-
- ///
- /// If there are any global variables, we need to emit those as well!
- /// There is something funny (and illegal) about calling the GetBsetObject from the static decl. Instead, we have to build a dummy
- /// routine first (or we get compiler errors).
- ///
- /// Make the accessor a property accessor so that we always call the Load. The reason for this is to make sure that we
- /// always get the latest version. For example, gDirectory is often switching and we will have to be able to deal with that
- /// here.
- ///
- /// We take a slightly different route for a non-TObject.
- ///
-
- if (type_has_globals(info.CPPName())) {
- auto &globals (list_of_globals_of_type(info.CPPName()));
- for (unsigned int i = 0; i < globals.size(); i++) {
- string loadCall;
- if (info.InheritsFromTObject()) {
- loadCall = "ROOTNET::Utility::ROOTObjectServices::GetBestObject<Interface::" + info.NETName() + "^>(::" + globals[i].Name() + ")";
- } else {
- loadCall = "gcnew " + info.NETName() + "(::" + globals[i].Name() + ")";
- }
-
- ///
- /// Now create the static property accessor
- ///
-
- string interfaceName = "Interface::" + info.NETName() + " ^";
- emitter.start_line() << "static property " << interfaceName << globals[i].Name() << endl;
- emitter.brace_open();
- emitter.start_line() << interfaceName << " get() { return " << loadCall << "; }" << endl;
- emitter.brace_close();
- }
- }
-
- ///
- /// If there are any feature methods, now is the time!
- ///
-
- FeatureManager::GetFeaturesFor(info).emit_header_method_definitions(info, emitter);
-
- ///
- /// Done with the class decl!
- ///
-
- emitter.brace_close(true);
-
- }
-
- ///
- /// Register this class with the monitor -- but only if it comes from TObject (because otherwise it isn't
- /// plugged into any of the framework).
- ///
- void ClassTranslator::emit_registration (const RootClassInfo &info, SourceEmitter &emitter, bool we_own)
- {
- if (info.CPPName() == "TObject" || info.InheritsFromTObject()) {
- emitter.start_line() << "ROOTNET::Utility::ROOTObjectManager::instance()->RegisterObject (_instance, this);" << endl;
- if (we_own) {
- emitter.start_line() << "_owner = true;" << endl;
- } else {
- emitter.start_line() << "_owner = false;" << endl;
- }
- }
- }
-
- namespace {
- /// Translate a single type from .NET to CPP. Return the variable name
- /// you should use in your code.
- string emit_translation_net_cpp (const string &var_name, // Name in .net world
- const CPPNetTypeMapper::TypeTranslator *trans, // the type pointer
- SourceEmitter &emitter)
- {
- if (!trans->requires_translation_to_cpp()) {
- return var_name;
- }
-
- string translated_argument ("trans_" + var_name);
- trans->translate_to_cpp (var_name, translated_argument, emitter);
- return translated_argument;
- }
-
- /// Emit the translators for each argument. Return a list of the argument names that can
- /// be used to do the actual calls.
- vector<string> emit_cpp_args (const vector<RootClassMethodArg> &args, SourceEmitter &emitter)
- {
- vector<string> cpp_argnames;
- for (unsigned int i = 0; i < args.size(); i++) {
- const CPPNetTypeMapper::TypeTranslator *trans = CPPNetTypeMapper::instance()->get_translator_from_cpp (args[i].CPPTypeName());
- cpp_argnames.push_back(emit_translation_net_cpp(args[i].get_argname(), trans, emitter));
- }
- return cpp_argnames;
- }
-
- void emit_translation_net_cpp_cleanup (const string &arg_name, // Used in .NET world
- const string &cpp_name, // Used in C++ world
- const CPPNetTypeMapper::TypeTranslator *trans,
- SourceEmitter &emitter)
- {
- if (trans->requires_cleanup_code()) {
- trans->translate_to_cpp_cleanup (arg_name, cpp_name, emitter);
- }
- }
-
- /// Issue the cleanup calls for argument names
- void clean_up_args (const vector<RootClassMethodArg> &args,
- const vector<string> &cpp_argnames,
- SourceEmitter &emitter)
- {
- for (unsigned int i = 0; i < args.size(); i++) {
- const CPPNetTypeMapper::TypeTranslator *trans = CPPNetTypeMapper::instance()->get_translator_from_cpp (args[i].CPPTypeName());
- emit_translation_net_cpp_cleanup (args[i].get_argname(), cpp_argnames[i], trans, emitter);
- }
- }
-
- string clean_up_net_name(const string &name)
- {
- string result = name;
- for (int i = 0; i < result.size(); i++) {
- if (result[i] == '-')
- result[i] = '_';
- if (result[i] == '>')
- result[i] = '_';
- }
- return result;
-
- }
-
- /// Emit code to issue a "return" statement of some sort
- void emit_return (const CPPNetTypeMapper::TypeTranslator *return_translator, const std::string &return_var, SourceEmitter &emitter, bool use_interface = false)
- {
- string return_var_name (return_var);
- if (return_translator->requires_translation_to_net()) {
- auto dotnet_return_var = clean_up_net_name ("dotnet_" + return_var);
- return_translator->translate_to_net (dotnet_return_var, return_var, emitter, use_interface);
- return_var_name = dotnet_return_var;
- }
- emitter.start_line() << "return " << return_var_name << ";" << endl;
- }
- }
-
- ///
- /// Write out the body of a function!
- ///
- void ClassTranslator::emit_function_body(const RootClassMethod &method, const RootClassInfo &info, SourceEmitter &emitter)
- {
- ///
- /// If we can't translate the method, we should bail out right away.
- ///
-
- if (method.IsAmbiguous()) {
- emitter.start_line() << "throw gcnew ::System::NotImplementedException(\"Method has ambiguous C++ resolution: not currently supported.\");" << endl;
- return;
- } else if (method.IsHidden()) {
- emitter.start_line() << "throw gcnew ::System::NotImplementedException(\"Method has is protected in C++ class: can't be accessed.\");" << endl;
- return;
- }
-
- ///
- /// Setup for return - we may not use it (this function may not demand it).
- ///
-
- string return_var ("f_abz_result"); // Common (potential) name.
-
- ///
- /// Translate the arguments into CPP land from .net land
- ///
-
- const vector<RootClassMethodArg> &args = method.arguments();
- vector<string> cpp_argnames (emit_cpp_args(args, emitter));
-
- ///
- /// Now that we have the arguments in hand, we can write the body of the function.
- ///
-
- if (method.IsCtor()) {
- emitter.start_line() << "_instance = new ::" << info.CPPName() << "(";
- for (unsigned int i = 0; i < cpp_argnames.size(); i++) {
- if (i != 0) {
- emitter() << ", ";
- }
- emitter() << cpp_argnames[i];
- }
- emitter() << ");" << endl;
-
- // If this has a super-class, we need to set the instance there.
- auto superInfo (info.GetBestClassToInherrit());
- if (superInfo.size() != 0) {
- auto &superInfoPtr = RootClassInfoCollection::GetRootClassInfo(superInfo);
- emitter.start_line() << superInfoPtr.NETName() << "::SetInstance(_instance);" << endl;
- }
-
- emit_registration(info, emitter, true);
- } else {
- const CPPNetTypeMapper::TypeTranslator *return_translator = method.get_return_type_translator();
- if (return_translator != 0) {
- emitter.start_line() << return_translator->cpp_code_typename() << " " << return_var << " = ";
- } else {
- emitter.start_line();
- }
-
- /// If static, then make the static call
- if (method.IsStatic()) {
- emitter() << "::" << method.ClassOfMethodDefinition() << "::" << method.NETName();
- } else {
- if (method.IsConst()) {
- emitter() << "((const " << info.CPPName() << " *) _instance)->";
- } else {
- emitter() << "_instance->";
- }
- emitter() << method.FullName();
- }
- emitter() << "(";
- for (unsigned int i = 0; i < cpp_argnames.size(); i++) {
- if (i != 0) {
- emitter() << ", ";
- }
- emitter() << cpp_argnames[i];
- }
- emitter() << ");" << endl;
- }
-
- ///
- /// Next, do any clean up required from the argument translation. I hope this is exception
- /// safe!!! (memory leaks bad!!).
- ///
-
- clean_up_args (args, cpp_argnames, emitter);
-
- ///
- /// If there is a return.
- ///
-
- if ((!method.IsAmbiguous() && !method.IsHidden()) && !method.IsCtor() && method.has_return_value()) {
- emit_return (method.get_return_type_translator(), return_var, emitter);
- }
- }
-
- namespace {
- // Get the operator out for a non-asignment operator ("operator+").
- string parse_nonassign_operator (const RootClassMethod &method)
- {
- auto name = method.NETName().substr(string("operator").length());
- return name;
- }
- }
-
- ///
- /// Generate the actual class.
- ///
- void ClassTranslator::generate_class_methods (RootClassInfo &info, SourceEmitter &emitter)
- {
- ///
- /// Make sure that we are dealing with nullptr's that we can understand!
- ///
-
- emitter() << "#ifdef nullptr" << endl;
- emitter() << "#undef nullptr" << endl;
- emitter() << "#endif" << endl;
-
- //
- // If this isn't a base class then we will need to reference other guys in the c-tors.
- //
-
- auto superClass = info.GetBestClassToInherrit();
- RootClassInfo *superClassInfo (nullptr);
- if (superClass.size() != 0) {
- superClassInfo = RootClassInfoCollection::GetRootClassInfoPtr(superClass);
- }
-
- ///
- /// First, do our particular c-tors...
- ///
-
- emitter.start_line() << info.NETName() << "::" << info.NETName() << "(::" << info.CPPName() << " *instance)" << endl;
- emitter.start_line() << " : _instance(instance)";
- if (superClassInfo != nullptr) {
- emitter() << ", " << superClassInfo->NETName() << "(_instance)";
- }
- emitter() << endl;
- emitter.brace_open();
- emit_registration(info, emitter, false);
- emitter.brace_close();
-
- emitter.start_line() << info.NETName() << "::" << info.NETName() << "(::" << info.CPPName() << " &instance)" << endl;
- emitter.start_line() << " : _instance(&instance)";
- if (superClassInfo != nullptr) {
- emitter() << ", " << superClassInfo->NETName() << "(_instance)";
- }
- emitter() << endl;
- emitter.brace_open();
- emit_registration(info, emitter, false);
- emitter.brace_close();
-
- ///
- /// Drop this object from registration info
- ///
-
- emitter.start_line() << "void " << info.NETName() << "::DropObjectFromTables (void)" << endl;
- emitter.brace_open();
- if (info.InheritsFromTObject() || info.CPPName() == "TObject") {
- emitter.start_line() << "ROOTNET::Utility::ROOTObjectManager::instance()->ForgetAboutObject(_instance);" << endl;
- }
- emitter.brace_close();
-
-
- ///
- /// Do all the public methods, ctors and etc. that we need to get done!
- /// Some methods have the same signature, differing only by a "const" or
- /// similar. So we make sure we don't write those out twice.
- ///
-
- set<string> written_methods;
- vector<RootClassMethod> arrayOperators;
- vector<RootClassMethod> mathOperators;
- const vector<RootClassMethod> &class_protos (info.GetAllPrototypesForThisClass(true));
- auto inh_classes(GetNonInherritedClasses(info));
-
- for (unsigned int i = 0; i < class_protos.size(); i++) {
- const RootClassMethod &method = class_protos[i];
-
- // Make sure this is a method we want to be implementing here!
- if (inh_classes.find(method.ClassOfMethodDefinition()) == inh_classes.end())
- continue;
-
- if (method.IsIndexer()) {
- arrayOperators.push_back(method);
- continue;
- }
-
- if (!method.IsGoodForClass()) {
- continue;
- }
-
- if (method.IsMathOperator()) {
- mathOperators.push_back(method);
- continue;
- }
-
- try {
- string n_header = method.generate_normalized_method_header();
- if (written_methods.find(n_header) != written_methods.end()) {
- continue;
- }
- written_methods.insert(n_header);
-
- if (method.has_return_value() && method.get_return_type_translator()->is_reference_to_object()) {
- continue;
- }
-
- emitter.start_line() << method.generate_method_header(true) << endl;
-
- if (method.IsCtor() && superClassInfo != nullptr) {
- emitter.start_line() << " : " << superClassInfo->NETName() << " ((::" << superClassInfo->CPPName() << "*) nullptr)" << endl;
- }
-
- emitter.brace_open();
- } catch (runtime_error &e) {
- cout << " translation failed (" << method.CPPName() << "): " << e.what() << endl;
- continue;
- }
-
- ///
- /// Great. Now do the call to actually make the "stuff" happy
- ///
-
- emit_function_body(method, info, emitter);
-
- ///
- /// Done with the method
- ///
-
- emitter.brace_close();
- emitter() << endl;
- }
-
- //
- // Do the math methods
- //
-
- for (vector<RootClassMethod>::const_iterator itr = mathOperators.begin(); itr != mathOperators.end(); itr++) {
- auto headers (generate_operator_header(*itr, true));
- for (vector<string>::const_iterator itr_h = headers.begin(); itr_h != headers.end(); itr_h++) {
- emitter.start_line() << *itr_h << endl;
- emitter.brace_open();
-
- // Get the CPP versions of the arguments
- const vector<RootClassMethodArg> &args = itr->arguments();
- vector<string> cpp_argnames (emit_cpp_args(args, emitter));
-
- // Now, do the calc
- auto op (parse_nonassign_operator(*itr));
-
- string return_var ("f_abc_return");
- const CPPNetTypeMapper::TypeTranslator *return_translator = itr->get_return_type_translator();
- if (return_translator != 0) {
- emitter.start_line() << return_translator->cpp_code_typename() << " " << return_var << " = ";
- } else {
- emitter.start_line();
- }
-
- if (cpp_argnames.size() == 0) { // Unary operator
- emitter() << op << " *(base_obj_a1->_instance)";
- } else { // Binary operator
- emitter() << "*(base_obj_a1->_instance) " << op << " " << cpp_argnames[0];
- }
- emitter() << ";" << endl;
-
- if (itr->has_return_value())
- emit_return (return_translator, return_var, emitter, false);
-
- emitter.brace_close();
- emitter() << endl;
- }
- }
-
- ///
- /// Emit any extra features
- ///
-
- FeatureManager::GetFeaturesFor(info).emit_class_methods(info, emitter);
-
- ///
- /// Do the properties for this object.
- ///
-
- const vector<RootClassProperty> &properties (info.GetProperties());
- for (vector<RootClassProperty>::const_iterator itr = properties.begin(); itr != properties.end(); itr++) {
- // Make sure at least one of these guys is defined in this thing
-
- if (!propertyShouldBeEmitted(*itr, inh_classes))
- continue;
-
- if (itr->isGetter()) {
- emitter.start_line() << itr->property_type() << " " << info.NETName() << "::" << itr->name() << "::get ()" << endl;
- emitter.brace_open();
- emit_function_body(*(itr->getter_method()), info, emitter);
- emitter.brace_close();
- }
- if (itr->isSetter()) {
- emitter.start_line() << "void " << info.NETName() << "::" << itr->name() << "::set (" << itr->property_type()
- << " " << itr->setter_method()->arguments()[0].get_argname() << ")" << endl;
- emitter.brace_open();
- emit_function_body(*(itr->setter_method()), info, emitter);
- emitter.brace_close();
- }
- }
-
- ///
- /// Emit fields for this object. Symantics are a little tricky here in the following sense - a field can
- /// have only a single type for get/set. If we the two types aren't the same then we are a little bit stuck. So
- /// we prefer the 'get' over the setup. That usually works out in the ROOT world.
- ///
-
- auto &fields (info.GetAllDataFields(true));
- for (int i = 0; i < fields.size(); i++) {
- const RootClassField &f(fields[i]);
-
- // Make sure this field is in the class we are working on here.
- if (inh_classes.find(f.ClassOfFieldDefinition()) == inh_classes.end())
- continue;
-
- if (f.GetterOK()) {
- emitter.start_line() << f.NETType() << " " << info.NETName() << "::" << f.NETName() << "::get ()" << endl;
- emitter.brace_open();
- emit_return(f.Translator(), "_instance->" + f.CPPName(), emitter);
- emitter.brace_close();
- }
-
- if (f.SetterOK()) {
- emitter.start_line() << "void " << info.NETName() << "::" << f.NETName()
- << "::set (" << f.NETType() << " f_xyz_val)" << endl;
- emitter.brace_open();
- auto tempname (emit_translation_net_cpp("f_xyz_val", f.Translator(), emitter));
- emitter.start_line() << " _instance->" << f.CPPName() << " = " << tempname << ";" << endl;
- emit_translation_net_cpp_cleanup("f_xyz_val", tempname, f.Translator(), emitter);
- emitter.brace_close();
- }
-
-
- }
-
- ///
- /// Deal with the indexer methods. This is like above, only we know exactly what we are
- /// writing so we can move a little faster with fewer if statements. :-)
- ///
- /// Indexers can suffer from covarient returns as well, so we need to double
- /// check which operator we are calling for indexing!
- ///
-
- vector<CPPIndexerInfo> indexers (SortIndexers(arrayOperators));
- for (unsigned int i = 0; i < indexers.size(); i++) {
- CPPIndexerInfo &iinfo (indexers[i]);
-
- emitter.start_line() << iinfo._return_type->net_return_type_name() << " " << info.NETName() << "::default::get(" << iinfo._index_type->net_interface_name() << " index)" << endl;
- emitter.brace_open();
- string arg_name (emit_translation_net_cpp ("index", iinfo._index_type, emitter));
- string return_val ("f_abc_return");
- emitter.start_line() << iinfo._return_type->cpp_code_typename() << " " << return_val << " = ";
- if (iinfo._method->ClassOfMethodDefinition() != info.CPPName()) {
- emitter() << " _instance->" << iinfo._method->ClassOfMethodDefinition() << "::operator[](" << arg_name << ");" << endl;
- } else {
- emitter() << " (*_instance)[" << arg_name << "];" << endl;
- }
-
- if (!iinfo._index_type->clean_up_matters_for_return_value_only()) {
- emit_translation_net_cpp_cleanup ("index", arg_name, iinfo._index_type, emitter);
- }
- emit_return (iinfo._return_type, return_val, emitter);
- emitter.brace_close();
-
- if (iinfo._is_setter) {
- emitter.start_line() << "void " << info.NETName() << "::default::set(" << iinfo._index_type->net_interface_name() << " index, " << iinfo._return_type->net_return_type_name() << " value)" << endl;
- emitter.brace_open();
- string arg_name (emit_translation_net_cpp ("index", iinfo._index_type, emitter));
- string value_name (emit_translation_net_cpp ("value", iinfo._return_type, emitter));
- emitter.start_line() << " (*_instance)[" << arg_name << "] = " << value_name << ";" << endl;
- if (!iinfo._index_type->clean_up_matters_for_return_value_only()) {
- emit_translation_net_cpp_cleanup ("index", arg_name, iinfo._index_type, emitter);
- }
- if (!iinfo._return_type->clean_up_matters_for_return_value_only()) {
- emit_translation_net_cpp_cleanup ("value", value_name, iinfo._return_type, emitter);
- }
- emitter.brace_close();
- }
- }
-
- }
-
- ///
- /// Check to see if there is a template in our past. If there is, we have to can doing this
- /// particular class translation.
- ///
- bool ClassTranslator::check_inheritance_list(const RootClassInfo &class_info)
- {
- ///
- /// One thing that is a little tricky is when a class has inherited from a template class. we can't deal with those
- /// currently.
- ///
-
- const vector<string> inherited_classes = class_info.GetDirectInheritedClasses();
- for (unsigned int i = 0; i < inherited_classes.size(); i++) {
- if (inherited_classes[i].find("<") != inherited_classes[i].npos) {
- return false;
- }
- }
-
- ///
- /// Ok -- good stuff!
- ///
-
- return true;
- }
-
- ///
- /// If there are some classes that can be selectivly removed from the inheritance list to enable
- /// translation, then do that!
- ///
- void ClassTranslator::clean_inheritance_list(RootClassInfo &info)
- {
- const vector<string> class_list (info.GetDirectInheritedClasses());
- for (unsigned int i = 0; i < class_list.size(); i++) {
- if (!CPPNetTypeMapper::instance()->has_mapping(class_list[i]))
- {
- info.RemoveInheritedClass(class_list[i]);
- }
- }
- }
-
- ///
- /// Returns the list of dependent library names for a particular library. Basically, all the other libraries that
- /// were used to draw includes from during this translation process.
- ///
- vector<string> ClassTranslator::get_dependent_libraries(const std::string &library_name) const
- {
- map<string, set<string> >::const_iterator lib_info = _library_dependencies.find(library_name);
- if (lib_info == _library_dependencies.end()) {
- return vector<string> ();
- }
- set<string> set_of_libs(lib_info->second);
- vector<string> result (set_of_libs.begin(), set_of_libs.end());
- return result;
- }
-
- ///
- /// Return a list of library names
- ///
- vector<string> ClassTranslator::get_all_library_names() const
- {
- vector<string> result;
- transform(_library_dependencies.begin(), _library_dependencies.end(), back_inserter(result),
- [] (const pair<string, set<string> > &item) {return item.first;});
-
- return result;
-
- }
-
- ///
- /// Always emit the headers in .cpp file -- default implementation is one very large library! :-)
- ///
- bool ClassTranslator::emit_this_header(const RootClassInfo &class_being_wrapped, const RootClassInfo &dependent_class)
- {
- return true;
- }
-
- ///
- /// We will write out a seperate file for an enum as long as it isn't a class enum
- ///
- bool ClassTranslator::emit_this_enum(const RootClassInfo &class_being_wrapped, const RootEnum &dependent_class)
- {
- return true;
- }
-
- ///
- /// Load up globals
- ///
- void ClassTranslator::load_globals(void)
- {
- _globals_by_type = ROOTHelpers::GetAllGlobals();
- }
-
- ///
- /// Does this type have any globals?
- ///
- bool ClassTranslator::type_has_globals (const std::string &type_name) const
- {
- map<string, vector<RootGlobalVariable> >::const_iterator itr = _globals_by_type.find(type_name);
- return itr != _globals_by_type.end();
- }
-
-
- ///
- /// List of all the type names for a type
- ///
- const vector<RootGlobalVariable> &ClassTranslator::list_of_globals_of_type(const std::string &type_name) const
- {
- map<string, vector<RootGlobalVariable> >::const_iterator itr = _globals_by_type.find(type_name);
- if (itr == _globals_by_type.end()) {
- static vector<RootGlobalVariable> bogus;
- return bogus;
- }
- return itr->second;
- }
-
- ///
- /// Translate an enum
- ///
- void ClassTranslator::translate(RootEnum &enum_info)
- {
- ostringstream hpp_filename;
- hpp_filename << _base_directory << "\\" << enum_info.NameUnqualified() << ".hpp";
-
- SourceEmitter hpp_emitter (hpp_filename.str());
- hpp_emitter() << "// Generated by ROOT Wrapper Generator" << endl;
- hpp_emitter() << "#pragma once" << endl;
-
- hpp_emitter.start_namespace ("ROOTNET");
-
- write_header_enum_standard (enum_info, hpp_emitter, true);
-
- hpp_emitter.brace_close();
- hpp_emitter.close();
-
- ostringstream cpp_filename;
- cpp_filename << _base_directory << "\\" << enum_info.NameUnqualified() << ".cpp";
-
- SourceEmitter cpp_emitter (cpp_filename.str());
- cpp_emitter() << "// Generated by ROOT Wrapper Generator" << endl;
- cpp_emitter.include_file(enum_info.NameUnqualified() + ".hpp");
- cpp_emitter.close();
- }
-
- namespace
- {
- //
- // Write out a single class for a global variable. yeah, I know, but at least it is totally
- // consistent and thus easy to find.
- //
- void write_header_global_variable (const CPPNetTypeMapper::TypeTranslator *trans, const RootGlobalVariable &global, SourceEmitter &hpp_emitter)
- {
- hpp_emitter.start_line() << endl;
- hpp_emitter.start_line() << "// " << trans->cpp_code_typename() << " " << global.Name() << endl;
- hpp_emitter.start_line() << "public ref class " << global.Name() << endl;
- hpp_emitter.brace_open();
- hpp_emitter.start_line() << "public:" << endl;
- hpp_emitter.start_line() << "static property " << trans->net_interface_name() << " Value" << endl;
- hpp_emitter.brace_open();
-
- // Do the get.
- hpp_emitter.start_line() << trans->net_interface_name() << " get()" << endl;
- hpp_emitter.brace_open();
- if (trans->requires_translation())
- {
- trans->translate_to_net("result", "::" + global.Name(), hpp_emitter);
- hpp_emitter.start_line() << "return result;" << endl;
- } else {
- hpp_emitter.start_line() << "return " << "::" << global.Name() << ";" << endl;
- }
- hpp_emitter.brace_close();
-
- hpp_emitter.start_line() << "void set(" << trans->net_interface_name() << " v)" << endl;
- hpp_emitter.brace_open();
- if (trans->requires_translation())
- {
- trans->translate_to_cpp("v", "cpp_v", hpp_emitter);
- hpp_emitter.start_line() << "::" << global.Name() << " = cpp_v;" << endl;
- } else {
- hpp_emitter.start_line() << "::" << global.Name() << " = v;" << endl;
- }
- hpp_emitter.brace_close();
-
- hpp_emitter.brace_close();
- hpp_emitter.brace_close(true);
- }
- }
-
- ///
- /// Generate a file that contains all the globals that
- /// we know about.
- ///
- void ClassTranslator::translate_global_variables(const string &libname)
- {
- //
- // Get the list of global variables we are going to work with.
- //
-
- vector<RootGlobalVariable> vars;
- for_each(_globals_by_type.begin(), _globals_by_type.end(), [&] (const pair<string, vector<RootGlobalVariable> > &global_list)
- {
- auto &myvars (vars);
- auto &mylibname (libname);
- for_each(global_list.second.begin(), global_list.second.end(), [&] (const RootGlobalVariable &global)
- {
- if (CPPNetTypeMapper::instance()->has_mapping(global.Type()) && (mylibname == "*" || mylibname == global.LibName()))
- {
- myvars.push_back(global);
- }
- });
- });
-
- //
- // Generate one header, and its matching cpp file.
- //
-
- ostringstream hpp_filename;
- hpp_filename << _base_directory << "\\Globals.hpp";
-
- SourceEmitter hpp_emitter (hpp_filename.str());
- hpp_emitter() << "// Generated by ROOT Wrapper Generator" << endl;
- hpp_emitter() << "#pragma once" << endl;
-
- //
- // Now include the header files we need to do this translation. There are two sets of headers we have to include.
- //
-
- set<string> requested_includes;
- for_each (vars.begin(), vars.end(), [&requested_includes] (const RootGlobalVariable &global)
- {
- auto includes = global.GetListOfIncludeFiles();
- for (int i = 0; i < includes.size(); i++) {
- requested_includes.insert(includes[i]);
- }
- });
-
- for_each(requested_includes.begin(), requested_includes.end(), [&hpp_emitter] (const string &include)
- {
- hpp_emitter.include_file(include);
- });
-
- //
- // We want a "special" namespace for all of this.
- //
-
- hpp_emitter.start_namespace ("ROOTNET");
- hpp_emitter.start_namespace ("Globals");
-
- //
- // Now loop through all of the types in there. Only do types we
- // know how to deal with.
- //
-
- for_each(vars.begin(), vars.end(), [&hpp_emitter] (const RootGlobalVariable &global)
- {
- auto translator = CPPNetTypeMapper::instance()->get_translator_from_cpp(global.Type());
- write_header_global_variable(translator, global, hpp_emitter);
- });
-
- hpp_emitter.brace_close();
- hpp_emitter.brace_close();
- hpp_emitter.close();
-
- //
- // Since everything is defined in the hpp file, we just deal with it
- // there. This is a .cpp file that is only necessary to include in the
- // build so that the .hpp file gets correctly sucked in.
- //
-
- ostringstream cpp_filename;
- cpp_filename << _base_directory << "\\Globals.cpp";
- SourceEmitter cpp_emitter (cpp_filename.str());
- cpp_emitter() << "// Generated by ROOT Wrapper Generator" << endl;
- cpp_emitter.include_file("Globals.hpp");
- cpp_emitter.close();
- }