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
34 redef fun identify_language
(n
) do return n
.is_cpp
36 redef fun compile_module_block
(block
, ecc
, nmodule
)
38 if nmodule
.cpp_file
== null then nmodule
.cpp_file
= new CPPCompilationUnit
40 if block
.is_cpp_header
then
41 nmodule
.cpp_file
.header_custom
.add
(block
.location
.as_line_pragma
)
42 nmodule
.cpp_file
.header_custom
.add
(block
.code
)
43 else if block
.is_cpp_body
then
44 nmodule
.cpp_file
.body_custom
.add
( block
.location
.as_line_pragma
)
45 nmodule
.cpp_file
.body_custom
.add
( block
.code
)
49 # We call C++ from C using 2 more files (_ffi.c and _ffi.cpp) and multiple generated functions:
50 # 1. The standard C implementation function (___impl) expected by the common FFI
51 # 2. The indirection function (___cpp_impl_mid) is a C function, called from C but implemented as `extern "C"` in C++
52 # 3. The actual C++ implementation function (___cpp_impl)
53 redef fun compile_extern_method
(block
, m
, ecc
, nmodule
)
55 if nmodule
.cpp_file
== null then nmodule
.cpp_file
= new CPPCompilationUnit
57 var mmodule
= nmodule
.mmodule
.as(not null)
58 var mclass_type
= m
.parent
.as(AClassdef).mclass
.mclass_type
59 var mproperty
= m
.mpropdef
.mproperty
61 # Signature of the indirection function implemented as `extern "C"` in C++
62 var indirection_sig
= mproperty
.build_csignature
(mclass_type
, mmodule
, "___cpp_impl_mid", long_signature
, internal_call_context
)
64 ## In C file (__ffi.c)
66 # Declare the indirection function in C
67 ecc
.body_decl
.add
("{indirection_sig};\n")
69 # Call the indirection function from C (___impl)
70 var fc
: CFunction = new ExternCFunction(m
, mmodule
)
71 fc
.exprs
.add
(mproperty
.build_ccall
(mclass_type
, mmodule
, "___cpp_impl_mid", long_signature
, cpp_call_context
, null))
73 ecc
.add_exported_function
( fc
)
75 ## In C++ file (__ffi.cpp)
77 # Declare the indirection function in C++
78 nmodule
.cpp_file
.header_decl
.add
("extern \"C\
" \{\n")
79 nmodule
.cpp_file
.header_decl
.add
("{indirection_sig};\n")
80 nmodule
.cpp_file
.header_decl
.add
("\}\n")
82 # Implement the indirection function as extern in C++
83 # Will convert C arguments to C++ and call the C++ implementation function.
84 fc
= new CFunction(indirection_sig
)
85 if not mproperty
.is_init
then
86 var param_name
= "recv"
87 var type_name
= to_cpp_call_context
.name_mtype
(mclass_type
)
88 if mclass_type
.mclass
.ftype
isa ForeignCppType then
89 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
91 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = {param_name};\n")
94 for param
in m
.mpropdef
.msignature
.mparameters
do
95 var param_name
= param
.name
96 var type_name
= to_cpp_call_context
.name_mtype
(param
.mtype
)
97 if mclass_type
.mclass
.ftype
isa ForeignCppType then
98 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
100 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = {param_name};\n")
103 fc
.exprs
.add
(mproperty
.build_ccall
(mclass_type
, mmodule
, "___cpp_impl", long_signature
, cpp_call_context
, "_for_cpp"))
105 nmodule
.cpp_file
.add_local_function
(fc
)
107 # Custom C++, the body of the Nit C++ method is copied to its own C++ function
108 var cpp_signature
= mproperty
.build_csignature
(mclass_type
, mmodule
, "___cpp_impl", long_signature
, cpp_call_context
)
109 fc
= new CFunction(cpp_signature
)
110 fc
.decls
.add
( block
.location
.as_line_pragma
)
111 fc
.exprs
.add
( block
.code
)
112 nmodule
.cpp_file
.add_local_function
( fc
)
115 redef fun compile_extern_class
(block
, m
, ecc
, nmodule
) do end
117 redef fun get_ftype
(block
, m
) do return new ForeignCppType(block
.code
)
119 redef fun compile_to_files
(nmodule
, compdir
)
121 var cpp_file
= nmodule
.cpp_file
122 assert cpp_file
!= null
124 # write .cpp and .hpp file
125 cpp_file
.header_custom
.add
("extern \"C\
" \{\n")
126 cpp_file
.header_custom
.add
("#include \"{nmodule.mmodule.name}._ffi
.h\
"\n")
127 cpp_file
.header_custom
.add
("\}\n")
129 var file
= cpp_file
.write_to_files
(nmodule
, compdir
)
131 # add complation to makefile
132 nmodule
.ffi_files
.add
(file
)
134 # add linked option to support C++
135 nmodule
.c_linker_options
= "{nmodule.c_linker_options} -lstdc++"
138 redef fun compile_callback
(callback
, nmodule
, mmodule
, ecc
)
140 callback
.compile_callback_to_cpp
(nmodule
, mmodule
)
144 redef class AExternCodeBlock
145 fun is_cpp
: Bool do return language_name
!= null and
146 (language_name_lowered
== "c++" or language_name_lowered
.has_prefix
("c++ "))
148 fun is_cpp_body
: Bool do return language_name
!= null and
149 (language_name_lowered
== "c++" or language_name_lowered
== "c++ body")
151 fun is_cpp_header
: Bool do return language_name
!= null and
152 (language_name_lowered
== "c++ header")
155 class CPPCompilationUnit
156 super CCompilationUnit
158 fun write_to_files
(amodule
: AModule, compdir
: String): ExternCppFile
160 var base_name
= "{amodule.mmodule.name}._ffi"
162 var h_file
= "{base_name}.hpp"
163 var guard
= "{amodule.cname.to_s.to_upper}_NIT_HPP"
165 write_header_to_file
(amodule
, "{compdir}/{h_file}", new Array[String], guard
)
167 var c_file
= "{base_name}.cpp"
168 write_body_to_file
(amodule
, "{compdir}/{c_file}", ["<string>", "<iostream>", "\"{h_file}\
""])
170 files
.add
("{compdir}/{c_file}")
172 return new ExternCppFile("{compdir}/{c_file}")
179 redef fun makefile_rule_name
do return "{filename.basename("")}.o"
180 redef fun makefile_rule_content
do return "g++ -c {filename.basename("")} -o {filename.basename("")}.o"
188 init (cpp_type
: String)
190 self.cpp_type
= cpp_type
194 redef class NitniCallback
195 fun compile_callback_to_cpp
(nmodule
: AModule, mmodule
: MModule) do end
199 private fun cpp_call_context
: CppCallContext do return once
new CppCallContext
200 private fun to_cpp_call_context
: ToCppCallContext do return once
new ToCppCallContext
201 private fun from_cpp_call_context
: FromCppCallContext do return once
new FromCppCallContext
204 redef class MExplicitCall
205 redef fun compile_callback_to_cpp
(nmodule
, mmodule
)
207 var mproperty
= mproperty
208 assert mproperty
isa MMethod
210 var cpp_signature
= mproperty
.build_csignature
(recv_mtype
, mmodule
, null, short_signature
, from_cpp_call_context
)
211 var ccall
= mproperty
.build_ccall
(recv_mtype
, mmodule
, null, long_signature
, from_cpp_call_context
, null)
212 var fc
= new CFunction(cpp_signature
)
214 nmodule
.cpp_file
.add_local_function
( fc
)
218 private class CppCallContext
221 redef fun name_mtype
(mtype
)
223 if mtype
isa MClassType then
224 var ftype
= mtype
.mclass
.ftype
225 if ftype
isa ForeignCppType then
226 return ftype
.cpp_type
234 class ToCppCallContext
237 redef fun cast_to
(mtype
, name
)
239 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignCppType then
240 return "(void*)({name})"
245 private class FromCppCallContext
248 redef fun cast_from
(mtype
, name
)
250 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignCppType then
251 return "static_cast<{name_mtype(mtype)}>({name})"