1 //===- CXTypes.cpp - Implements 'CXTypes' aspect of libclang ------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===--------------------------------------------------------------------===//
9 //
10 // This file implements the 'CXTypes' API hooks in the Clang-C library.
11 //
12 //===--------------------------------------------------------------------===//
13
14 #include "CIndexer.h"
15 #include "CXCursor.h"
16 #include "CXString.h"
17 #include "CXTranslationUnit.h"
18 #include "CXType.h"
19 #include "clang/AST/Decl.h"
20 #include "clang/AST/DeclObjC.h"
21 #include "clang/AST/DeclTemplate.h"
22 #include "clang/AST/Expr.h"
23 #include "clang/AST/Type.h"
24 #include "clang/Frontend/ASTUnit.h"
25
26 using namespace clang;
27
GetBuiltinTypeKind(const BuiltinType * BT)28 static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) {
29 #define BTCASE(K) case BuiltinType::K: return CXType_##K
30 switch (BT->getKind()) {
31 BTCASE(Void);
32 BTCASE(Bool);
33 BTCASE(Char_U);
34 BTCASE(UChar);
35 BTCASE(Char16);
36 BTCASE(Char32);
37 BTCASE(UShort);
38 BTCASE(UInt);
39 BTCASE(ULong);
40 BTCASE(ULongLong);
41 BTCASE(UInt128);
42 BTCASE(Char_S);
43 BTCASE(SChar);
44 case BuiltinType::WChar_S: return CXType_WChar;
45 case BuiltinType::WChar_U: return CXType_WChar;
46 BTCASE(Short);
47 BTCASE(Int);
48 BTCASE(Long);
49 BTCASE(LongLong);
50 BTCASE(Int128);
51 BTCASE(Float);
52 BTCASE(Double);
53 BTCASE(LongDouble);
54 BTCASE(NullPtr);
55 BTCASE(Overload);
56 BTCASE(Dependent);
57 BTCASE(ObjCId);
58 BTCASE(ObjCClass);
59 BTCASE(ObjCSel);
60 default:
61 return CXType_Unexposed;
62 }
63 #undef BTCASE
64 }
65
GetTypeKind(QualType T)66 static CXTypeKind GetTypeKind(QualType T) {
67 const Type *TP = T.getTypePtrOrNull();
68 if (!TP)
69 return CXType_Invalid;
70
71 #define TKCASE(K) case Type::K: return CXType_##K
72 switch (TP->getTypeClass()) {
73 case Type::Builtin:
74 return GetBuiltinTypeKind(cast<BuiltinType>(TP));
75 TKCASE(Complex);
76 TKCASE(Pointer);
77 TKCASE(BlockPointer);
78 TKCASE(LValueReference);
79 TKCASE(RValueReference);
80 TKCASE(Record);
81 TKCASE(Enum);
82 TKCASE(Typedef);
83 TKCASE(ObjCInterface);
84 TKCASE(ObjCObjectPointer);
85 TKCASE(FunctionNoProto);
86 TKCASE(FunctionProto);
87 TKCASE(ConstantArray);
88 TKCASE(Vector);
89 default:
90 return CXType_Unexposed;
91 }
92 #undef TKCASE
93 }
94
95
MakeCXType(QualType T,CXTranslationUnit TU)96 CXType cxtype::MakeCXType(QualType T, CXTranslationUnit TU) {
97 CXTypeKind TK = CXType_Invalid;
98
99 if (TU && !T.isNull()) {
100 ASTContext &Ctx = cxtu::getASTUnit(TU)->getASTContext();
101 if (Ctx.getLangOpts().ObjC1) {
102 QualType UnqualT = T.getUnqualifiedType();
103 if (Ctx.isObjCIdType(UnqualT))
104 TK = CXType_ObjCId;
105 else if (Ctx.isObjCClassType(UnqualT))
106 TK = CXType_ObjCClass;
107 else if (Ctx.isObjCSelType(UnqualT))
108 TK = CXType_ObjCSel;
109 }
110 }
111 if (TK == CXType_Invalid)
112 TK = GetTypeKind(T);
113
114 CXType CT = { TK, { TK == CXType_Invalid ? 0 : T.getAsOpaquePtr(), TU }};
115 return CT;
116 }
117
118 using cxtype::MakeCXType;
119
GetQualType(CXType CT)120 static inline QualType GetQualType(CXType CT) {
121 return QualType::getFromOpaquePtr(CT.data[0]);
122 }
123
GetTU(CXType CT)124 static inline CXTranslationUnit GetTU(CXType CT) {
125 return static_cast<CXTranslationUnit>(CT.data[1]);
126 }
127
128 extern "C" {
129
clang_getCursorType(CXCursor C)130 CXType clang_getCursorType(CXCursor C) {
131 using namespace cxcursor;
132
133 CXTranslationUnit TU = cxcursor::getCursorTU(C);
134 if (!TU)
135 return MakeCXType(QualType(), TU);
136
137 ASTContext &Context = cxtu::getASTUnit(TU)->getASTContext();
138 if (clang_isExpression(C.kind)) {
139 QualType T = cxcursor::getCursorExpr(C)->getType();
140 return MakeCXType(T, TU);
141 }
142
143 if (clang_isDeclaration(C.kind)) {
144 const Decl *D = cxcursor::getCursorDecl(C);
145 if (!D)
146 return MakeCXType(QualType(), TU);
147
148 if (const TypeDecl *TD = dyn_cast<TypeDecl>(D))
149 return MakeCXType(Context.getTypeDeclType(TD), TU);
150 if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D))
151 return MakeCXType(Context.getObjCInterfaceType(ID), TU);
152 if (const ValueDecl *VD = dyn_cast<ValueDecl>(D))
153 return MakeCXType(VD->getType(), TU);
154 if (const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D))
155 return MakeCXType(PD->getType(), TU);
156 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
157 return MakeCXType(FD->getType(), TU);
158 return MakeCXType(QualType(), TU);
159 }
160
161 if (clang_isReference(C.kind)) {
162 switch (C.kind) {
163 case CXCursor_ObjCSuperClassRef: {
164 QualType T
165 = Context.getObjCInterfaceType(getCursorObjCSuperClassRef(C).first);
166 return MakeCXType(T, TU);
167 }
168
169 case CXCursor_ObjCClassRef: {
170 QualType T = Context.getObjCInterfaceType(getCursorObjCClassRef(C).first);
171 return MakeCXType(T, TU);
172 }
173
174 case CXCursor_TypeRef: {
175 QualType T = Context.getTypeDeclType(getCursorTypeRef(C).first);
176 return MakeCXType(T, TU);
177
178 }
179
180 case CXCursor_CXXBaseSpecifier:
181 return cxtype::MakeCXType(getCursorCXXBaseSpecifier(C)->getType(), TU);
182
183 case CXCursor_MemberRef:
184 return cxtype::MakeCXType(getCursorMemberRef(C).first->getType(), TU);
185
186 case CXCursor_VariableRef:
187 return cxtype::MakeCXType(getCursorVariableRef(C).first->getType(), TU);
188
189 case CXCursor_ObjCProtocolRef:
190 case CXCursor_TemplateRef:
191 case CXCursor_NamespaceRef:
192 case CXCursor_OverloadedDeclRef:
193 default:
194 break;
195 }
196
197 return MakeCXType(QualType(), TU);
198 }
199
200 return MakeCXType(QualType(), TU);
201 }
202
clang_getTypeSpelling(CXType CT)203 CXString clang_getTypeSpelling(CXType CT) {
204 QualType T = GetQualType(CT);
205 if (T.isNull())
206 return cxstring::createEmpty();
207
208 CXTranslationUnit TU = GetTU(CT);
209 SmallString<64> Str;
210 llvm::raw_svector_ostream OS(Str);
211 PrintingPolicy PP(cxtu::getASTUnit(TU)->getASTContext().getLangOpts());
212
213 T.print(OS, PP);
214
215 return cxstring::createDup(OS.str());
216 }
217
clang_getTypedefDeclUnderlyingType(CXCursor C)218 CXType clang_getTypedefDeclUnderlyingType(CXCursor C) {
219 using namespace cxcursor;
220 CXTranslationUnit TU = cxcursor::getCursorTU(C);
221
222 if (clang_isDeclaration(C.kind)) {
223 const Decl *D = cxcursor::getCursorDecl(C);
224
225 if (const TypedefNameDecl *TD = dyn_cast_or_null<TypedefNameDecl>(D)) {
226 QualType T = TD->getUnderlyingType();
227 return MakeCXType(T, TU);
228 }
229
230 return MakeCXType(QualType(), TU);
231 }
232
233 return MakeCXType(QualType(), TU);
234 }
235
clang_getEnumDeclIntegerType(CXCursor C)236 CXType clang_getEnumDeclIntegerType(CXCursor C) {
237 using namespace cxcursor;
238 CXTranslationUnit TU = cxcursor::getCursorTU(C);
239
240 if (clang_isDeclaration(C.kind)) {
241 const Decl *D = cxcursor::getCursorDecl(C);
242
243 if (const EnumDecl *TD = dyn_cast_or_null<EnumDecl>(D)) {
244 QualType T = TD->getIntegerType();
245 return MakeCXType(T, TU);
246 }
247
248 return MakeCXType(QualType(), TU);
249 }
250
251 return MakeCXType(QualType(), TU);
252 }
253
clang_getEnumConstantDeclValue(CXCursor C)254 long long clang_getEnumConstantDeclValue(CXCursor C) {
255 using namespace cxcursor;
256
257 if (clang_isDeclaration(C.kind)) {
258 const Decl *D = cxcursor::getCursorDecl(C);
259
260 if (const EnumConstantDecl *TD = dyn_cast_or_null<EnumConstantDecl>(D)) {
261 return TD->getInitVal().getSExtValue();
262 }
263
264 return LLONG_MIN;
265 }
266
267 return LLONG_MIN;
268 }
269
clang_getEnumConstantDeclUnsignedValue(CXCursor C)270 unsigned long long clang_getEnumConstantDeclUnsignedValue(CXCursor C) {
271 using namespace cxcursor;
272
273 if (clang_isDeclaration(C.kind)) {
274 const Decl *D = cxcursor::getCursorDecl(C);
275
276 if (const EnumConstantDecl *TD = dyn_cast_or_null<EnumConstantDecl>(D)) {
277 return TD->getInitVal().getZExtValue();
278 }
279
280 return ULLONG_MAX;
281 }
282
283 return ULLONG_MAX;
284 }
285
clang_getFieldDeclBitWidth(CXCursor C)286 int clang_getFieldDeclBitWidth(CXCursor C) {
287 using namespace cxcursor;
288
289 if (clang_isDeclaration(C.kind)) {
290 const Decl *D = getCursorDecl(C);
291
292 if (const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(D)) {
293 if (FD->isBitField())
294 return FD->getBitWidthValue(getCursorContext(C));
295 }
296 }
297
298 return -1;
299 }
300
clang_getCanonicalType(CXType CT)301 CXType clang_getCanonicalType(CXType CT) {
302 if (CT.kind == CXType_Invalid)
303 return CT;
304
305 QualType T = GetQualType(CT);
306 CXTranslationUnit TU = GetTU(CT);
307
308 if (T.isNull())
309 return MakeCXType(QualType(), GetTU(CT));
310
311 return MakeCXType(cxtu::getASTUnit(TU)->getASTContext()
312 .getCanonicalType(T),
313 TU);
314 }
315
clang_isConstQualifiedType(CXType CT)316 unsigned clang_isConstQualifiedType(CXType CT) {
317 QualType T = GetQualType(CT);
318 return T.isLocalConstQualified();
319 }
320
clang_isVolatileQualifiedType(CXType CT)321 unsigned clang_isVolatileQualifiedType(CXType CT) {
322 QualType T = GetQualType(CT);
323 return T.isLocalVolatileQualified();
324 }
325
clang_isRestrictQualifiedType(CXType CT)326 unsigned clang_isRestrictQualifiedType(CXType CT) {
327 QualType T = GetQualType(CT);
328 return T.isLocalRestrictQualified();
329 }
330
clang_getPointeeType(CXType CT)331 CXType clang_getPointeeType(CXType CT) {
332 QualType T = GetQualType(CT);
333 const Type *TP = T.getTypePtrOrNull();
334
335 if (!TP)
336 return MakeCXType(QualType(), GetTU(CT));
337
338 switch (TP->getTypeClass()) {
339 case Type::Pointer:
340 T = cast<PointerType>(TP)->getPointeeType();
341 break;
342 case Type::BlockPointer:
343 T = cast<BlockPointerType>(TP)->getPointeeType();
344 break;
345 case Type::LValueReference:
346 case Type::RValueReference:
347 T = cast<ReferenceType>(TP)->getPointeeType();
348 break;
349 case Type::ObjCObjectPointer:
350 T = cast<ObjCObjectPointerType>(TP)->getPointeeType();
351 break;
352 default:
353 T = QualType();
354 break;
355 }
356 return MakeCXType(T, GetTU(CT));
357 }
358
clang_getTypeDeclaration(CXType CT)359 CXCursor clang_getTypeDeclaration(CXType CT) {
360 if (CT.kind == CXType_Invalid)
361 return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound);
362
363 QualType T = GetQualType(CT);
364 const Type *TP = T.getTypePtrOrNull();
365
366 if (!TP)
367 return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound);
368
369 Decl *D = 0;
370
371 try_again:
372 switch (TP->getTypeClass()) {
373 case Type::Typedef:
374 D = cast<TypedefType>(TP)->getDecl();
375 break;
376 case Type::ObjCObject:
377 D = cast<ObjCObjectType>(TP)->getInterface();
378 break;
379 case Type::ObjCInterface:
380 D = cast<ObjCInterfaceType>(TP)->getDecl();
381 break;
382 case Type::Record:
383 case Type::Enum:
384 D = cast<TagType>(TP)->getDecl();
385 break;
386 case Type::TemplateSpecialization:
387 if (const RecordType *Record = TP->getAs<RecordType>())
388 D = Record->getDecl();
389 else
390 D = cast<TemplateSpecializationType>(TP)->getTemplateName()
391 .getAsTemplateDecl();
392 break;
393
394 case Type::InjectedClassName:
395 D = cast<InjectedClassNameType>(TP)->getDecl();
396 break;
397
398 // FIXME: Template type parameters!
399
400 case Type::Elaborated:
401 TP = cast<ElaboratedType>(TP)->getNamedType().getTypePtrOrNull();
402 goto try_again;
403
404 default:
405 break;
406 }
407
408 if (!D)
409 return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound);
410
411 return cxcursor::MakeCXCursor(D, GetTU(CT));
412 }
413
clang_getTypeKindSpelling(enum CXTypeKind K)414 CXString clang_getTypeKindSpelling(enum CXTypeKind K) {
415 const char *s = 0;
416 #define TKIND(X) case CXType_##X: s = "" #X ""; break
417 switch (K) {
418 TKIND(Invalid);
419 TKIND(Unexposed);
420 TKIND(Void);
421 TKIND(Bool);
422 TKIND(Char_U);
423 TKIND(UChar);
424 TKIND(Char16);
425 TKIND(Char32);
426 TKIND(UShort);
427 TKIND(UInt);
428 TKIND(ULong);
429 TKIND(ULongLong);
430 TKIND(UInt128);
431 TKIND(Char_S);
432 TKIND(SChar);
433 case CXType_WChar: s = "WChar"; break;
434 TKIND(Short);
435 TKIND(Int);
436 TKIND(Long);
437 TKIND(LongLong);
438 TKIND(Int128);
439 TKIND(Float);
440 TKIND(Double);
441 TKIND(LongDouble);
442 TKIND(NullPtr);
443 TKIND(Overload);
444 TKIND(Dependent);
445 TKIND(ObjCId);
446 TKIND(ObjCClass);
447 TKIND(ObjCSel);
448 TKIND(Complex);
449 TKIND(Pointer);
450 TKIND(BlockPointer);
451 TKIND(LValueReference);
452 TKIND(RValueReference);
453 TKIND(Record);
454 TKIND(Enum);
455 TKIND(Typedef);
456 TKIND(ObjCInterface);
457 TKIND(ObjCObjectPointer);
458 TKIND(FunctionNoProto);
459 TKIND(FunctionProto);
460 TKIND(ConstantArray);
461 TKIND(Vector);
462 }
463 #undef TKIND
464 return cxstring::createRef(s);
465 }
466
clang_equalTypes(CXType A,CXType B)467 unsigned clang_equalTypes(CXType A, CXType B) {
468 return A.data[0] == B.data[0] && A.data[1] == B.data[1];;
469 }
470
clang_isFunctionTypeVariadic(CXType X)471 unsigned clang_isFunctionTypeVariadic(CXType X) {
472 QualType T = GetQualType(X);
473 if (T.isNull())
474 return 0;
475
476 if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>())
477 return (unsigned)FD->isVariadic();
478
479 if (T->getAs<FunctionNoProtoType>())
480 return 1;
481
482 return 0;
483 }
484
clang_getFunctionTypeCallingConv(CXType X)485 CXCallingConv clang_getFunctionTypeCallingConv(CXType X) {
486 QualType T = GetQualType(X);
487 if (T.isNull())
488 return CXCallingConv_Invalid;
489
490 if (const FunctionType *FD = T->getAs<FunctionType>()) {
491 #define TCALLINGCONV(X) case CC_##X: return CXCallingConv_##X
492 switch (FD->getCallConv()) {
493 TCALLINGCONV(Default);
494 TCALLINGCONV(C);
495 TCALLINGCONV(X86StdCall);
496 TCALLINGCONV(X86FastCall);
497 TCALLINGCONV(X86ThisCall);
498 TCALLINGCONV(X86Pascal);
499 TCALLINGCONV(AAPCS);
500 TCALLINGCONV(AAPCS_VFP);
501 TCALLINGCONV(PnaclCall);
502 TCALLINGCONV(IntelOclBicc);
503 }
504 #undef TCALLINGCONV
505 }
506
507 return CXCallingConv_Invalid;
508 }
509
clang_getNumArgTypes(CXType X)510 int clang_getNumArgTypes(CXType X) {
511 QualType T = GetQualType(X);
512 if (T.isNull())
513 return -1;
514
515 if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) {
516 return FD->getNumArgs();
517 }
518
519 if (T->getAs<FunctionNoProtoType>()) {
520 return 0;
521 }
522
523 return -1;
524 }
525
clang_getArgType(CXType X,unsigned i)526 CXType clang_getArgType(CXType X, unsigned i) {
527 QualType T = GetQualType(X);
528 if (T.isNull())
529 return MakeCXType(QualType(), GetTU(X));
530
531 if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) {
532 unsigned numArgs = FD->getNumArgs();
533 if (i >= numArgs)
534 return MakeCXType(QualType(), GetTU(X));
535
536 return MakeCXType(FD->getArgType(i), GetTU(X));
537 }
538
539 return MakeCXType(QualType(), GetTU(X));
540 }
541
clang_getResultType(CXType X)542 CXType clang_getResultType(CXType X) {
543 QualType T = GetQualType(X);
544 if (T.isNull())
545 return MakeCXType(QualType(), GetTU(X));
546
547 if (const FunctionType *FD = T->getAs<FunctionType>())
548 return MakeCXType(FD->getResultType(), GetTU(X));
549
550 return MakeCXType(QualType(), GetTU(X));
551 }
552
clang_getCursorResultType(CXCursor C)553 CXType clang_getCursorResultType(CXCursor C) {
554 if (clang_isDeclaration(C.kind)) {
555 const Decl *D = cxcursor::getCursorDecl(C);
556 if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D))
557 return MakeCXType(MD->getResultType(), cxcursor::getCursorTU(C));
558
559 return clang_getResultType(clang_getCursorType(C));
560 }
561
562 return MakeCXType(QualType(), cxcursor::getCursorTU(C));
563 }
564
clang_isPODType(CXType X)565 unsigned clang_isPODType(CXType X) {
566 QualType T = GetQualType(X);
567 if (T.isNull())
568 return 0;
569
570 CXTranslationUnit TU = GetTU(X);
571
572 return T.isPODType(cxtu::getASTUnit(TU)->getASTContext()) ? 1 : 0;
573 }
574
clang_getElementType(CXType CT)575 CXType clang_getElementType(CXType CT) {
576 QualType ET = QualType();
577 QualType T = GetQualType(CT);
578 const Type *TP = T.getTypePtrOrNull();
579
580 if (TP) {
581 switch (TP->getTypeClass()) {
582 case Type::ConstantArray:
583 ET = cast<ConstantArrayType> (TP)->getElementType();
584 break;
585 case Type::Vector:
586 ET = cast<VectorType> (TP)->getElementType();
587 break;
588 case Type::Complex:
589 ET = cast<ComplexType> (TP)->getElementType();
590 break;
591 default:
592 break;
593 }
594 }
595 return MakeCXType(ET, GetTU(CT));
596 }
597
clang_getNumElements(CXType CT)598 long long clang_getNumElements(CXType CT) {
599 long long result = -1;
600 QualType T = GetQualType(CT);
601 const Type *TP = T.getTypePtrOrNull();
602
603 if (TP) {
604 switch (TP->getTypeClass()) {
605 case Type::ConstantArray:
606 result = cast<ConstantArrayType> (TP)->getSize().getSExtValue();
607 break;
608 case Type::Vector:
609 result = cast<VectorType> (TP)->getNumElements();
610 break;
611 default:
612 break;
613 }
614 }
615 return result;
616 }
617
clang_getArrayElementType(CXType CT)618 CXType clang_getArrayElementType(CXType CT) {
619 QualType ET = QualType();
620 QualType T = GetQualType(CT);
621 const Type *TP = T.getTypePtrOrNull();
622
623 if (TP) {
624 switch (TP->getTypeClass()) {
625 case Type::ConstantArray:
626 ET = cast<ConstantArrayType> (TP)->getElementType();
627 break;
628 default:
629 break;
630 }
631 }
632 return MakeCXType(ET, GetTU(CT));
633 }
634
clang_getArraySize(CXType CT)635 long long clang_getArraySize(CXType CT) {
636 long long result = -1;
637 QualType T = GetQualType(CT);
638 const Type *TP = T.getTypePtrOrNull();
639
640 if (TP) {
641 switch (TP->getTypeClass()) {
642 case Type::ConstantArray:
643 result = cast<ConstantArrayType> (TP)->getSize().getSExtValue();
644 break;
645 default:
646 break;
647 }
648 }
649 return result;
650 }
651
clang_getDeclObjCTypeEncoding(CXCursor C)652 CXString clang_getDeclObjCTypeEncoding(CXCursor C) {
653 if (!clang_isDeclaration(C.kind))
654 return cxstring::createEmpty();
655
656 const Decl *D = cxcursor::getCursorDecl(C);
657 ASTContext &Ctx = cxcursor::getCursorContext(C);
658 std::string encoding;
659
660 if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) {
661 if (Ctx.getObjCEncodingForMethodDecl(OMD, encoding))
662 return cxstring::createRef("?");
663 } else if (const ObjCPropertyDecl *OPD = dyn_cast<ObjCPropertyDecl>(D))
664 Ctx.getObjCEncodingForPropertyDecl(OPD, NULL, encoding);
665 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
666 Ctx.getObjCEncodingForFunctionDecl(FD, encoding);
667 else {
668 QualType Ty;
669 if (const TypeDecl *TD = dyn_cast<TypeDecl>(D))
670 Ty = Ctx.getTypeDeclType(TD);
671 if (const ValueDecl *VD = dyn_cast<ValueDecl>(D))
672 Ty = VD->getType();
673 else return cxstring::createRef("?");
674 Ctx.getObjCEncodingForType(Ty, encoding);
675 }
676
677 return cxstring::createDup(encoding);
678 }
679
680 } // end: extern "C"
681