Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for friend functions #649

Merged
merged 6 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Add `Info.friendly` to have `Parser` map some `friend` functions to Java methods ([pull #649](https://github.com/bytedeco/javacpp/pull/649))
* Add `Loader.loadProperties(boolean forceReload)` to reset platform properties ([issue deepjavalibrary/djl#2318](https://github.com/deepjavalibrary/djl/issues/2318))
* Prevent `TokenIndexer` from recursively expanding macros
* Fix `Generator` passing empty `String` objects on callback for arguments using adapters
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/bytedeco/javacpp/tools/Info.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public Info(Info i) {
define = i.define;
enumerate = i.enumerate;
flatten = i.flatten;
friendly = i.friendly;
immutable = i.immutable;
beanify = i.beanify;
objectify = i.objectify;
Expand Down Expand Up @@ -97,6 +98,9 @@ public Info(Info i) {
/** Outputs declarations for this class into their subclasses as well.
* Also adds methods for explicit casting, as done for multiple inheritance by default. */
boolean flatten = false;
/** Maps friend functions. Only functions having in their argument list an instance of the class they are friend
* of are currently supported. They are mapped as instance methods of the class. */
boolean friendly = false;
/** Disables generation of setters for public data members of a class */
boolean immutable = false;
/** Adds JavaBeans-style prefixes to getters and setters of public data members of a class */
Expand Down Expand Up @@ -136,6 +140,8 @@ public Info(Info i) {
public Info enumerate(boolean enumerate) { this.enumerate = enumerate; return this; }
public Info flatten() { this.flatten = true; return this; }
public Info flatten(boolean flatten) { this.flatten = flatten; return this; }
public Info friendly() { this.friendly = true; return this; }
public Info friendly(boolean friendly) { this.friendly = friendly; return this; }
public Info immutable() { this.immutable = true; return this; }
public Info immutable(boolean immutable) { this.immutable = immutable; return this; }
public Info beanify() { this.beanify = true; return this; }
Expand Down
59 changes: 51 additions & 8 deletions src/main/java/org/bytedeco/javacpp/tools/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2114,9 +2114,7 @@ Parameters parameters(Context context, int infoNumber, boolean useDefaults) thro
params.list += "/*" + defaultToken + defaultValue + "*/";
}
params.signature += '_';
for (char c : dcl.type.javaName.substring(dcl.type.javaName.lastIndexOf(' ') + 1).toCharArray()) {
params.signature += Character.isJavaIdentifierPart(c) ? c : '_';
}
params.signature += dcl.type.signature();
params.names += (count > 1 ? ", " : "") + paramName;
if (dcl.javaName.startsWith("arg")) {
try {
Expand Down Expand Up @@ -2331,8 +2329,10 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
break;
}
}
if (type.friend || tokens.get().match("&&") || (context.javaName == null && localNamespace > 0) || (info != null && info.skip)) {
// this is a friend declaration, an rvalue function, or a member function definition or specialization, skip over
Info info2 = infoMap.getFirst(null);
boolean friendly = info != null ? info.friendly : info2 != null ? info2.friendly : false;
if ((type.friend && !friendly) || tokens.get().match("&&") || (context.javaName == null && localNamespace > 0) || (info != null && info.skip)) {
// this is an unwanted friend declaration, an rvalue function, or a member function definition or specialization, skip over
while (!tokens.get().match(':', '{', ';', Token.EOF)) {
tokens.next();
}
Expand Down Expand Up @@ -2376,6 +2376,11 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
decl.function = true;
declList.add(decl);
return true;
} else if (type.friend) {
// The friend function may be accessible only through ADL, so disable namespace lookup.
// Map the function to a private static method and create a public Java instance method below
// that calls this static method.
modifiers = "private static native @Namespace ";
} else if (type.staticMember || context.javaName == null) {
modifiers = "public " + ((info != null && info.objectify) || context.objectify ? "" : "static ") + "native ";
if (tokens.isCFile) {
Expand All @@ -2387,6 +2392,7 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
boolean first = true;
for (int n = -2; n < Integer.MAX_VALUE; n++) {
decl = new Declaration();
Declaration extraDecl = null; // Secondary declaration to add. Currently only used for friends.
tokens.index = startIndex;
boolean useDefaults = (info == null || !info.skipDefaults) && n % 2 != 0;
if ((type.constructor || type.destructor || type.operator) && params != null) {
Expand Down Expand Up @@ -2512,8 +2518,43 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
tokens.next();
}

// skip over non-const function within const class
if (!decl.constMember && context.constName != null) {
/* If it's a friend function, and we want friend mapping, check if it accepts an argument of the enclosing type.
If it does, add a Java instance method calling the static native method.
If it does not, skip over. */
if (type.friend && friendly) {
Declarator[] paramDeclarators = dcl.parameters.declarators;
String signature = dcl.javaName;
String argList = "", staticArgList = "";
boolean foundThis = false;
for (Declarator paramDecl : paramDeclarators) {
if (staticArgList.length() > 0) {
staticArgList += ", ";
}
if (!foundThis && paramDecl.type.cppName.equals(context.cppName)) {
foundThis = true;
staticArgList += "this";
} else {
if (argList.length() > 0) {
argList += ", ";
}
argList += paramDecl.type.javaName + " " + paramDecl.javaName;
signature += '_' + paramDecl.type.signature();
staticArgList += paramDecl.javaName;
}
}
if (foundThis) {
extraDecl = new Declaration();
extraDecl.signature = signature;
extraDecl.declarator = dcl; // Used in group to recognize friends
extraDecl.text = "public " + dcl.type.javaName + " " + dcl.javaName + "(" + argList + ") { "
+ (dcl.type.javaName.equals("void") ? "" : "return ") + dcl.javaName + "(" + staticArgList + "); }\n";
} else {
friendly = false;
}
}

// skip over friend functions we can't map and non-const function within const class
if ((type.friend && !friendly) || !decl.constMember && context.constName != null) {
decl.text = spacing;
declList.add(decl);
return true;
Expand Down Expand Up @@ -2582,6 +2623,7 @@ boolean function(Context context, DeclarationList declList) throws ParserExcepti
if (dcl.javaName.length() > 0 && !found && (!type.destructor || (info != null && info.javaText != null))) {
if (declList.add(decl, fullname)) {
first = false;
if (extraDecl != null) declList.add(extraDecl);
}
if (type.virtual && context.virtualize) {
break;
Expand Down Expand Up @@ -3614,7 +3656,8 @@ boolean group(Context context, DeclarationList declList) throws ParserException
}
}
for (Declaration d : declList2) {
if (!d.inaccessible && (d.declarator == null || d.declarator.type == null
if ((!d.inaccessible || d.declarator != null && d.declarator.type.friend)
&& (d.declarator == null || d.declarator.type == null
|| !d.declarator.type.constructor || !abstractClass || (info != null && info.virtualize))) {
decl.text += d.text;
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/bytedeco/javacpp/tools/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,12 @@ class Type {
return false;
}
}

String signature() {
String sig = "";
for (char c : javaName.substring(javaName.lastIndexOf(' ') + 1).toCharArray()) {
sig += Character.isJavaIdentifierPart(c) ? c : '_';
}
return sig;
}
}