1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Supports the use of the C++ language through the FFI
23 redef class FFILanguageAssignationPhase
24 var cpp_language
: FFILanguage = new CPPLanguage(self)
28 private var cpp_file
: nullable CPPCompilationUnit = null
30 # Custom options for the C++ compiler (CPPFLAGS)
31 var cppflags
= new MultiHashMap[String, String]
37 redef fun identify_language
(n
) do return n
.is_cpp
39 redef fun compile_module_block
(block
, ecc
, mmodule
)
41 if mmodule
.cpp_file
== null then mmodule
.cpp_file
= new CPPCompilationUnit
43 if block
.is_cpp_header
then
44 mmodule
.cpp_file
.header_custom
.add
(block
.location
.as_line_pragma
)
45 mmodule
.cpp_file
.header_custom
.add
(block
.code
)
46 else if block
.is_cpp_body
then
47 mmodule
.cpp_file
.body_custom
.add
( block
.location
.as_line_pragma
)
48 mmodule
.cpp_file
.body_custom
.add
( block
.code
)
52 # We call C++ from C using 2 more files (_ffi.c and _ffi.cpp) and multiple generated functions:
53 # 1. The standard C implementation function (___impl) expected by the common FFI
54 # 2. The indirection function (___cpp_impl_mid) is a C function, called from C but implemented as `extern "C"` in C++
55 # 3. The actual C++ implementation function (___cpp_impl)
56 redef fun compile_extern_method
(block
, m
, ecc
, mmodule
)
58 if mmodule
.cpp_file
== null then mmodule
.cpp_file
= new CPPCompilationUnit
60 var mclass_type
= m
.parent
.as(AClassdef).mclass
.mclass_type
61 var mproperty
= m
.mpropdef
.mproperty
63 # Signature of the indirection function implemented as `extern "C"` in C++
64 var indirection_sig
= mproperty
.build_csignature
(mclass_type
, mmodule
, "___cpp_impl_mid", long_signature
, internal_call_context
)
66 ## In C file (__ffi.c)
68 # Declare the indirection function in C
69 ecc
.body_decl
.add
("{indirection_sig};\n")
71 # Call the indirection function from C (___impl)
72 var fc
: CFunction = new ExternCFunction(m
, mmodule
)
73 fc
.exprs
.add
(mproperty
.build_ccall
(mclass_type
, mmodule
, "___cpp_impl_mid", long_signature
, cpp_call_context
, null))
75 ecc
.add_exported_function
( fc
)
77 ## In C++ file (__ffi.cpp)
79 # Declare the indirection function in C++
80 mmodule
.cpp_file
.header_decl
.add
("extern \"C\
" \{\n")
81 mmodule
.cpp_file
.header_decl
.add
("{indirection_sig};\n")
82 mmodule
.cpp_file
.header_decl
.add
("\}\n")
84 # Implement the indirection function as extern in C++
85 # Will convert C arguments to C++ and call the C++ implementation function.
86 fc
= new CFunction(indirection_sig
)
87 if not mproperty
.is_init
then
88 var param_name
= "self"
89 var type_name
= to_cpp_call_context
.name_mtype
(mclass_type
)
90 if mclass_type
.mclass
.ftype
isa ForeignCppType then
91 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
93 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = {param_name};\n")
96 for param
in m
.mpropdef
.msignature
.mparameters
do
97 var param_name
= param
.name
98 var type_name
= to_cpp_call_context
.name_mtype
(param
.mtype
)
99 if mclass_type
.mclass
.ftype
isa ForeignCppType then
100 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
102 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = {param_name};\n")
105 fc
.exprs
.add
(mproperty
.build_ccall
(mclass_type
, mmodule
, "___cpp_impl", long_signature
, cpp_call_context
, "_for_cpp"))
107 mmodule
.cpp_file
.add_exported_function
(fc
)
109 # Custom C++, the body of the Nit C++ method is copied to its own C++ function
110 var cpp_signature
= mproperty
.build_csignature
(mclass_type
, mmodule
, "___cpp_impl", long_signature
, cpp_call_context
)
111 fc
= new CFunction(cpp_signature
)
112 fc
.decls
.add
( block
.location
.as_line_pragma
)
113 fc
.exprs
.add
( block
.code
)
114 mmodule
.cpp_file
.add_local_function
( fc
)
117 redef fun compile_extern_class
(block
, m
, ecc
, mmodule
) do end
119 redef fun get_ftype
(block
, m
) do return new ForeignCppType(block
.code
)
121 redef fun compile_to_files
(mmodule
, compdir
)
123 var cpp_file
= mmodule
.cpp_file
124 assert cpp_file
!= null
126 # write .cpp and .hpp file
127 cpp_file
.header_custom
.add
("extern \"C\
" \{\n")
128 cpp_file
.header_custom
.add
("#include \"{mmodule.c_name}._ffi
.h\
"\n")
129 cpp_file
.header_custom
.add
("\}\n")
131 var file
= cpp_file
.write_to_files
(mmodule
, compdir
)
133 # add complation to makefile
134 mmodule
.ffi_files
.add
(file
)
136 # add linked option to support C++
137 mmodule
.ldflags
.add_one
("", "-lstdc++")
140 redef fun compile_callback
(callback
, mmodule
, mainmodule
, ecc
)
142 callback
.compile_callback_to_cpp
(mmodule
, mainmodule
)
146 redef class AExternCodeBlock
147 fun is_cpp
: Bool do return language_name
!= null and
148 (language_name_lowered
== "c++" or language_name_lowered
.has_prefix
("c++ "))
150 fun is_cpp_body
: Bool do return language_name
!= null and
151 (language_name_lowered
== "c++" or language_name_lowered
== "c++ body")
153 fun is_cpp_header
: Bool do return language_name
!= null and
154 (language_name_lowered
== "c++ header")
157 class CPPCompilationUnit
158 super CCompilationUnit
160 fun write_to_files
(mmodule
: MModule, compdir
: String): ExternCppFile
162 var base_name
= "{mmodule.c_name}._ffi"
164 var h_file
= "{base_name}.hpp"
165 var guard
= "{mmodule.c_name.to_s.to_upper}_NIT_HPP"
167 write_header_to_file
(mmodule
, "{compdir}/{h_file}", new Array[String], guard
)
169 var c_file
= "{base_name}.cpp"
170 write_body_to_file
(mmodule
, "{compdir}/{c_file}", ["<string>", "<iostream>", "\"{h_file}\
""])
172 files
.add
("{compdir}/{c_file}")
174 return new ExternCppFile("{compdir}/{c_file}", mmodule
)
183 redef fun makefile_rule_name
do return "{filename.basename}.o"
184 redef fun makefile_rule_content
do return "$(CXX) $(CFLAGS) {mmodule.cppflags[""].join(" ")} -c {filename.basename} -o {filename.basename}.o"
185 redef fun compiles_to_o_file
do return true
194 redef class NitniCallback
195 fun compile_callback_to_cpp
(mmodule
: MModule, mainmodule
: MModule) do end
198 private fun cpp_call_context
: CppCallContext do return once
new CppCallContext
199 private fun to_cpp_call_context
: ToCppCallContext do return once
new ToCppCallContext
200 private fun from_cpp_call_context
: FromCppCallContext do return once
new FromCppCallContext
202 redef class MExplicitCall
203 redef fun compile_callback_to_cpp
(mmodule
, mainmodule
)
205 var mproperty
= mproperty
206 assert mproperty
isa MMethod
208 var cpp_signature
= mproperty
.build_csignature
(recv_mtype
, mainmodule
, null, short_signature
, from_cpp_call_context
)
209 var ccall
= mproperty
.build_ccall
(recv_mtype
, mainmodule
, null, long_signature
, from_cpp_call_context
, null)
210 var fc
= new CFunction(cpp_signature
)
212 mmodule
.cpp_file
.add_local_function
( fc
)
216 private class CppCallContext
219 redef fun name_mtype
(mtype
)
221 if mtype
isa MClassType then
222 var ftype
= mtype
.mclass
.ftype
223 if ftype
isa ForeignCppType then
224 return ftype
.cpp_type
232 class ToCppCallContext
235 redef fun cast_to
(mtype
, name
)
237 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignCppType then
238 return "(void*)({name})"
243 private class FromCppCallContext
246 redef fun cast_from
(mtype
, name
)
248 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignCppType then
249 return "static_cast<{name_mtype(mtype)}>({name})"