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 var cpp_compiler_options
= "" is writable
36 redef fun identify_language
(n
) do return n
.is_cpp
38 redef fun compile_module_block
(block
, ecc
, mmodule
)
40 if mmodule
.cpp_file
== null then mmodule
.cpp_file
= new CPPCompilationUnit
42 if block
.is_cpp_header
then
43 mmodule
.cpp_file
.header_custom
.add
(block
.location
.as_line_pragma
)
44 mmodule
.cpp_file
.header_custom
.add
(block
.code
)
45 else if block
.is_cpp_body
then
46 mmodule
.cpp_file
.body_custom
.add
( block
.location
.as_line_pragma
)
47 mmodule
.cpp_file
.body_custom
.add
( block
.code
)
51 # We call C++ from C using 2 more files (_ffi.c and _ffi.cpp) and multiple generated functions:
52 # 1. The standard C implementation function (___impl) expected by the common FFI
53 # 2. The indirection function (___cpp_impl_mid) is a C function, called from C but implemented as `extern "C"` in C++
54 # 3. The actual C++ implementation function (___cpp_impl)
55 redef fun compile_extern_method
(block
, m
, ecc
, mmodule
)
57 if mmodule
.cpp_file
== null then mmodule
.cpp_file
= new CPPCompilationUnit
59 var mclass_type
= m
.parent
.as(AClassdef).mclass
.mclass_type
60 var mproperty
= m
.mpropdef
.mproperty
62 # Signature of the indirection function implemented as `extern "C"` in C++
63 var indirection_sig
= mproperty
.build_csignature
(mclass_type
, mmodule
, "___cpp_impl_mid", long_signature
, internal_call_context
)
65 ## In C file (__ffi.c)
67 # Declare the indirection function in C
68 ecc
.body_decl
.add
("{indirection_sig};\n")
70 # Call the indirection function from C (___impl)
71 var fc
: CFunction = new ExternCFunction(m
, mmodule
)
72 fc
.exprs
.add
(mproperty
.build_ccall
(mclass_type
, mmodule
, "___cpp_impl_mid", long_signature
, cpp_call_context
, null))
74 ecc
.add_exported_function
( fc
)
76 ## In C++ file (__ffi.cpp)
78 # Declare the indirection function in C++
79 mmodule
.cpp_file
.header_decl
.add
("extern \"C\
" \{\n")
80 mmodule
.cpp_file
.header_decl
.add
("{indirection_sig};\n")
81 mmodule
.cpp_file
.header_decl
.add
("\}\n")
83 # Implement the indirection function as extern in C++
84 # Will convert C arguments to C++ and call the C++ implementation function.
85 fc
= new CFunction(indirection_sig
)
86 if not mproperty
.is_init
then
87 var param_name
= "recv"
88 var type_name
= to_cpp_call_context
.name_mtype
(mclass_type
)
89 if mclass_type
.mclass
.ftype
isa ForeignCppType then
90 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
92 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = {param_name};\n")
95 for param
in m
.mpropdef
.msignature
.mparameters
do
96 var param_name
= param
.name
97 var type_name
= to_cpp_call_context
.name_mtype
(param
.mtype
)
98 if mclass_type
.mclass
.ftype
isa ForeignCppType then
99 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
101 fc
.exprs
.add
("{type_name} {param_name}_for_cpp = {param_name};\n")
104 fc
.exprs
.add
(mproperty
.build_ccall
(mclass_type
, mmodule
, "___cpp_impl", long_signature
, cpp_call_context
, "_for_cpp"))
106 mmodule
.cpp_file
.add_exported_function
(fc
)
108 # Custom C++, the body of the Nit C++ method is copied to its own C++ function
109 var cpp_signature
= mproperty
.build_csignature
(mclass_type
, mmodule
, "___cpp_impl", long_signature
, cpp_call_context
)
110 fc
= new CFunction(cpp_signature
)
111 fc
.decls
.add
( block
.location
.as_line_pragma
)
112 fc
.exprs
.add
( block
.code
)
113 mmodule
.cpp_file
.add_local_function
( fc
)
116 redef fun compile_extern_class
(block
, m
, ecc
, mmodule
) do end
118 redef fun get_ftype
(block
, m
) do return new ForeignCppType(block
.code
)
120 redef fun compile_to_files
(mmodule
, compdir
)
122 var cpp_file
= mmodule
.cpp_file
123 assert cpp_file
!= null
125 # write .cpp and .hpp file
126 cpp_file
.header_custom
.add
("extern \"C\
" \{\n")
127 cpp_file
.header_custom
.add
("#include \"{mmodule.name}._ffi
.h\
"\n")
128 cpp_file
.header_custom
.add
("\}\n")
130 var file
= cpp_file
.write_to_files
(mmodule
, compdir
)
132 # add complation to makefile
133 mmodule
.ffi_files
.add
(file
)
135 # add linked option to support C++
136 mmodule
.c_linker_options
= "{mmodule.c_linker_options} -lstdc++"
139 redef fun compile_callback
(callback
, mmodule
, mainmodule
, ecc
)
141 callback
.compile_callback_to_cpp
(mmodule
, mainmodule
)
145 redef class AExternCodeBlock
146 fun is_cpp
: Bool do return language_name
!= null and
147 (language_name_lowered
== "c++" or language_name_lowered
.has_prefix
("c++ "))
149 fun is_cpp_body
: Bool do return language_name
!= null and
150 (language_name_lowered
== "c++" or language_name_lowered
== "c++ body")
152 fun is_cpp_header
: Bool do return language_name
!= null and
153 (language_name_lowered
== "c++ header")
156 class CPPCompilationUnit
157 super CCompilationUnit
159 fun write_to_files
(mmodule
: MModule, compdir
: String): ExternCppFile
161 var base_name
= "{mmodule.name}._ffi"
163 var h_file
= "{base_name}.hpp"
164 var guard
= "{mmodule.cname.to_s.to_upper}_NIT_HPP"
166 write_header_to_file
(mmodule
, "{compdir}/{h_file}", new Array[String], guard
)
168 var c_file
= "{base_name}.cpp"
169 write_body_to_file
(mmodule
, "{compdir}/{c_file}", ["<string>", "<iostream>", "\"{h_file}\
""])
171 files
.add
("{compdir}/{c_file}")
173 return new ExternCppFile("{compdir}/{c_file}", mmodule
)
182 redef fun makefile_rule_name
do return "{filename.basename("")}.o"
183 redef fun makefile_rule_content
do return "$(CXX) $(CFLAGS) {mmodule.cpp_compiler_options} -c {filename.basename("")} -o {filename.basename("")}.o"
184 redef fun compiles_to_o_file
do return true
193 redef class NitniCallback
194 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
203 redef class MExplicitCall
204 redef fun compile_callback_to_cpp
(mmodule
, mainmodule
)
206 var mproperty
= mproperty
207 assert mproperty
isa MMethod
209 var cpp_signature
= mproperty
.build_csignature
(recv_mtype
, mainmodule
, null, short_signature
, from_cpp_call_context
)
210 var ccall
= mproperty
.build_ccall
(recv_mtype
, mainmodule
, null, long_signature
, from_cpp_call_context
, null)
211 var fc
= new CFunction(cpp_signature
)
213 mmodule
.cpp_file
.add_local_function
( fc
)
217 private class CppCallContext
220 redef fun name_mtype
(mtype
)
222 if mtype
isa MClassType then
223 var ftype
= mtype
.mclass
.ftype
224 if ftype
isa ForeignCppType then
225 return ftype
.cpp_type
233 class ToCppCallContext
236 redef fun cast_to
(mtype
, name
)
238 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignCppType then
239 return "(void*)({name})"
244 private class FromCppCallContext
247 redef fun cast_from
(mtype
, name
)
249 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignCppType then
250 return "static_cast<{name_mtype(mtype)}>({name})"