Add a C++ client for presents.
It provides code generators for streamables, services and receivers, but not DObject. It uses Apple's CFNetwork for its sockets and builds with XCode, so it's limited to OS X and iOS. It should be straightforward to replace both to make it cross-platform. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6264 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.threerings.presents.tools.InvocationTask.ServiceMethod;
|
||||
|
||||
public class CPPArgBuilder
|
||||
{
|
||||
public CPPArgBuilder(boolean ignoreFirst)
|
||||
{
|
||||
_ignoreFirst = ignoreFirst;
|
||||
}
|
||||
|
||||
public String getArguments (ServiceMethod meth)
|
||||
{
|
||||
return getArguments(meth, "");
|
||||
}
|
||||
|
||||
public String getArguments (ServiceMethod meth, String prefix)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder(prefix);
|
||||
Type[] ptypes = meth.method.getGenericParameterTypes();
|
||||
for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) {
|
||||
if (buf.length() > 0) {
|
||||
buf.append(", ");
|
||||
}
|
||||
buf.append(CPPUtil.getCPPType(ptypes[ii])).append(" arg").append(ii);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public List<String> getArgumentNames (ServiceMethod meth)
|
||||
{
|
||||
Type[] ptypes = meth.method.getGenericParameterTypes();
|
||||
List<String> args = Lists.newArrayListWithCapacity(ptypes.length - 1);
|
||||
for (int ii = 1; ii < ptypes.length; ii++) {
|
||||
args.add("arg" + ii);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
public String getArgumentsFromVector (ServiceMethod meth)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Type[] ptypes = meth.method.getGenericParameterTypes();
|
||||
for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) {
|
||||
if (buf.length() > 0) {
|
||||
buf.append(", ");
|
||||
}
|
||||
CPPType type = new CPPType(ptypes[ii]);
|
||||
buf.append(type.getCastFromStreamable("args[" + ii + "]"));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public List<String> getServiceArguments (ServiceMethod meth)
|
||||
{
|
||||
Type[] ptypes = meth.method.getGenericParameterTypes();
|
||||
List<String> args = Lists.newArrayListWithCapacity(ptypes.length);
|
||||
for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) {
|
||||
args.add(new CPPType(ptypes[ii]).getAsStreamable("arg" + ii));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
protected final boolean _ignoreFirst;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CPPField
|
||||
{
|
||||
public final String name;
|
||||
public final CPPType type;
|
||||
public final String reader;
|
||||
public final String writer;
|
||||
|
||||
public CPPField (Field f)
|
||||
throws IOException
|
||||
{
|
||||
type = new CPPType(f.getGenericType());
|
||||
name = f.getName().startsWith("_") ? f.getName().substring(1) : f.getName();
|
||||
if (type.fixed != null) {
|
||||
reader = type.cast + "(in.read" + type.interpreter + "(" + type.fixed + "))";
|
||||
} else if (type.interpreter.equals("Field")) {
|
||||
reader = "in.readField< " + type.getWithoutShared() + " >()";
|
||||
} else {
|
||||
reader = type.cast + "(in.read" + type.interpreter + "())";
|
||||
}
|
||||
writer = "out.write" + type.interpreter + "(" + name
|
||||
+ (type.fixed == null ? "" : ", " + type.fixed) + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.threerings.io.Streamable;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
public class CPPType
|
||||
{
|
||||
public static final String JAVA_LIST_FIXED = "JAVA_LIST_NAME()";
|
||||
|
||||
/** The full cpp type. */
|
||||
public final String type;
|
||||
|
||||
/** The method on ObjectInputStream and ObjectOutputStream to interpret this type. */
|
||||
public final String interpreter;
|
||||
|
||||
/** CPP code to cast from this object to its reader type. */
|
||||
public final String cast;
|
||||
|
||||
/** If this is a fixed type and thereby doesn't need a type encoding on the wire. */
|
||||
public final String fixed;
|
||||
|
||||
/** Another type embedded in this type, or null if it doesn't have an embedded type. */
|
||||
public final CPPType dependent;
|
||||
|
||||
public final String representationImport;
|
||||
|
||||
public final Class<?> rawType;
|
||||
|
||||
public final boolean primitive;
|
||||
|
||||
public CPPType (Type javaType)
|
||||
{
|
||||
if (javaType instanceof ParameterizedType) {
|
||||
rawType = (Class<?>)((ParameterizedType)javaType).getRawType();
|
||||
} else if (javaType instanceof TypeVariable<?>) {
|
||||
rawType = (Class<?>)((TypeVariable<?>)javaType).getBounds()[0];
|
||||
} else {
|
||||
rawType = (Class<?>)javaType;
|
||||
}
|
||||
|
||||
if (rawType.equals(List.class)) {
|
||||
fixed = JAVA_LIST_FIXED;
|
||||
} else {
|
||||
fixed = null;
|
||||
}
|
||||
|
||||
if (rawType.isArray()) {
|
||||
Class<?> componentType = rawType.getComponentType();
|
||||
dependent = new CPPType(componentType);
|
||||
type = "Shared< std::vector< " + dependent.type + " > >";
|
||||
interpreter = componentType.equals(Object.class) ||
|
||||
componentType.isPrimitive() ? "Field" : "Object";
|
||||
representationImport = null;
|
||||
|
||||
} else {
|
||||
type = CPPUtil.getCPPType(javaType);
|
||||
interpreter = getCPPInterpreter(rawType);
|
||||
if (javaType instanceof ParameterizedType) {
|
||||
Type[] typeArguments = ((ParameterizedType)javaType).getActualTypeArguments();
|
||||
if (rawType.equals(Comparable.class)) {
|
||||
dependent = new CPPType(Streamable.class);
|
||||
representationImport = "presents/Streamable.h";
|
||||
} else if (rawType == List.class) {
|
||||
if (!Streamable.class.isAssignableFrom((Class<?>)typeArguments[0])) {
|
||||
throw new IllegalArgumentException(
|
||||
"Lists may only contain Streamables in C++, not '" + typeArguments[0]
|
||||
+ "'");
|
||||
}
|
||||
dependent = new CPPType(typeArguments[0]);
|
||||
representationImport = null;
|
||||
} else if (rawType.equals(DSet.class)) {
|
||||
dependent = null;
|
||||
representationImport = CPPUtil.makePath(rawType, ".h");
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to handle " + rawType);
|
||||
}
|
||||
} else if (javaType instanceof TypeVariable<?>
|
||||
|| javaType.equals(com.threerings.presents.dobj.DSet.Entry.class)) {
|
||||
dependent = new CPPType(Streamable.class);
|
||||
representationImport = "presents/Streamable.h";
|
||||
} else {
|
||||
if (!Streamable.class.equals(rawType) && Streamable.class.isAssignableFrom(rawType)) {
|
||||
representationImport = CPPUtil.makePath(rawType, ".h");
|
||||
} else {
|
||||
representationImport = null;
|
||||
}
|
||||
dependent = null;
|
||||
}
|
||||
}
|
||||
|
||||
primitive = rawType.isPrimitive();
|
||||
Matcher m = TYPE_EXTRACT.matcher(type);
|
||||
cast = m.matches() ? "boost::static_pointer_cast<" + m.group(1) + ">" : type;
|
||||
}
|
||||
|
||||
protected String getCPPInterpreter (Class<?> ftype)
|
||||
{
|
||||
if (ftype.equals(String.class) || ftype.equals(List.class)) {
|
||||
return "Field";
|
||||
} else if (ftype.equals(Boolean.TYPE)) {
|
||||
return "Boolean";
|
||||
} else if (ftype.equals(Byte.TYPE)) {
|
||||
return "Byte";
|
||||
} else if (ftype.equals(Short.TYPE)) {
|
||||
return "Short";
|
||||
} else if (ftype.equals(Integer.TYPE)) {
|
||||
return "Int";
|
||||
} else if (ftype.equals(Long.TYPE)) {
|
||||
return "Long";
|
||||
} else if (ftype.equals(Float.TYPE)) {
|
||||
return "Float";
|
||||
} else if (ftype.equals(Double.TYPE)) {
|
||||
return "Double";
|
||||
} else {
|
||||
return "Object";
|
||||
}
|
||||
}
|
||||
|
||||
public String getCastFromStreamable (String name)
|
||||
{
|
||||
if (primitive) {
|
||||
return "boost::static_pointer_cast<presents::box::Boxed" + interpreter + ">(" + name + ")->value";
|
||||
}
|
||||
return cast + "(" + name + ")";
|
||||
}
|
||||
|
||||
public String getAsStreamable (String name)
|
||||
{
|
||||
if (primitive) {
|
||||
return "presents::box::Boxed" + interpreter + "::createShared(" + name + ")";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getWithoutShared ()
|
||||
{
|
||||
Matcher m = TYPE_EXTRACT.matcher(type);
|
||||
return m.matches() ? m.group(1) : type;
|
||||
}
|
||||
|
||||
protected static final Pattern TYPE_EXTRACT = Pattern.compile("Shared<(.*)>");
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.Resources;
|
||||
import com.samskivert.mustache.Mustache;
|
||||
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
public class CPPUtil
|
||||
{
|
||||
public static String getCPPType (Type ftype)
|
||||
{
|
||||
if (ftype.equals(com.threerings.presents.dobj.DSet.Entry.class)) {
|
||||
return "Shared<Streamable>";
|
||||
}
|
||||
if (ftype instanceof ParameterizedType) {
|
||||
Type[] typeArguments = ((ParameterizedType)ftype).getActualTypeArguments();
|
||||
Type raw = ((ParameterizedType)ftype).getRawType();
|
||||
if (raw.equals(Comparable.class)) {
|
||||
return "Shared<Streamable>";
|
||||
} else if (raw.equals(List.class)) {
|
||||
return "Shared< std::vector< " + getCPPType(typeArguments[0]) + " > >";
|
||||
} else if (raw.equals(DSet.class)) {
|
||||
ftype = raw;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to handle " + raw);
|
||||
}
|
||||
}
|
||||
|
||||
if (ftype.equals(Boolean.class) || ftype.equals(Byte.class) || ftype.equals(Short.class)
|
||||
|| ftype.equals(Integer.class) || ftype.equals(Long.class)
|
||||
|| ftype.equals(Float.class) || ftype.equals(Double.class)) {
|
||||
throw new IllegalArgumentException("Presents can't handle boxed types in C++");
|
||||
}
|
||||
if (ftype.equals(String.class)) {
|
||||
return "Shared<utf8>";
|
||||
} else if (ftype.equals(Boolean.TYPE)) {
|
||||
return "bool";
|
||||
} else if (ftype.equals(Byte.TYPE)) {
|
||||
return "int8";
|
||||
} else if (ftype.equals(Short.TYPE)) {
|
||||
return "int16";
|
||||
} else if (ftype.equals(Integer.TYPE)) {
|
||||
return "int32";
|
||||
} else if (ftype.equals(Long.TYPE)) {
|
||||
return "int64";
|
||||
} else if (ftype.equals(Float.TYPE)) {
|
||||
return "float";
|
||||
} else if (ftype.equals(Double.TYPE)) {
|
||||
return "double";
|
||||
} else if (ftype.equals(Object.class) || ftype instanceof TypeVariable<?>) {
|
||||
return "Shared<Streamable>";
|
||||
} else {
|
||||
return "Shared<" + makeCPPName((Class<?>)ftype) + ">";
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeTemplate (String tmplPath, File root, String path,
|
||||
Map<String, Object> ctx)
|
||||
throws IOException
|
||||
{
|
||||
String tmpl =
|
||||
Resources.toString(Resources.getResource(tmplPath), Charsets.UTF_8);
|
||||
File file = new File(root, path);
|
||||
file.getParentFile().mkdirs();
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(file));
|
||||
Mustache.compiler().escapeHTML(false).compile(tmpl).execute(ctx, out);
|
||||
out.close();
|
||||
}
|
||||
|
||||
public static String makeCPPName (Class<?> sclass)
|
||||
{
|
||||
return makeCPPName(makeNamespaces(sclass), sclass.getSimpleName());
|
||||
}
|
||||
|
||||
public static String makeCPPName (List<String> namespaces, String className)
|
||||
{
|
||||
return Joiner.on("::").join(namespaces) + "::" + className;
|
||||
}
|
||||
|
||||
public static String makePath (Class<?> klass, String ext)
|
||||
{
|
||||
return makePath(makeNamespaces(klass), klass.getSimpleName(), ext);
|
||||
}
|
||||
|
||||
public static String makePath (List<String> namespaces, String className, String ext)
|
||||
{
|
||||
return Joiner.on(File.separator).join(namespaces) + File.separator + className+ ext;
|
||||
}
|
||||
|
||||
public static List<String> makeNamespaces (String pack)
|
||||
{
|
||||
Iterable<String> split = Splitter.on(".").split(pack);
|
||||
List<String> segs = Lists.newArrayList(split);
|
||||
if (segs.size() > 1 && segs.get(0).equals("com") && segs.get(1).equals("threerings")) {
|
||||
segs.remove(0);
|
||||
segs.remove(0);
|
||||
}
|
||||
return segs;
|
||||
|
||||
}
|
||||
|
||||
public static List<String> makeNamespaces (Class<?> sclass)
|
||||
{
|
||||
if (sclass.getPackage() == null) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return makeNamespaces(sclass.getPackage().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static String makeNamespace (Class<?> sclass)
|
||||
{
|
||||
return Joiner.on("::").join(makeNamespaces(sclass));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import com.threerings.presents.tools.GenReceiverTask;
|
||||
|
||||
public class GenCPPReceiverTask extends GenReceiverTask
|
||||
{
|
||||
public void setCpproot (File asroot)
|
||||
{
|
||||
_cpproot = asroot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateDecoder (Class<?> receiver, File source, String rname, String rpackage,
|
||||
List<ServiceMethod> methods, Iterator<String> imports, String rcode)
|
||||
throws Exception
|
||||
{
|
||||
String dname = rname.replace("Receiver", "Decoder");
|
||||
|
||||
Map<String, Object> ctx = Maps.newHashMap();
|
||||
List<String> namespaces = CPPUtil.makeNamespaces(receiver);
|
||||
ctx.put("receiverName", rname);
|
||||
ctx.put("decoderName", dname);
|
||||
ctx.put("namespaces", namespaces);
|
||||
ctx.put("namespace", CPPUtil.makeNamespace(receiver));
|
||||
ctx.put("package", rpackage);
|
||||
ctx.put("methods", MethodDescriptor.from(methods));
|
||||
ctx.put("receiverCode", rcode);
|
||||
ctx.put("argbuilder", new CPPArgBuilder(false));
|
||||
|
||||
CPPUtil.writeTemplate(DECODER_HEADER_TMPL, _cpproot,
|
||||
CPPUtil.makePath(namespaces, dname, ".h"), ctx);
|
||||
|
||||
Set<String> receiverHeaderIncludes = Sets.newHashSet();
|
||||
Set<String> decoderImplIncludes = Sets.newHashSet();
|
||||
for (ServiceMethod meth : methods) {
|
||||
for (Type type : meth.method.getGenericParameterTypes()) {
|
||||
CPPType cppType = new CPPType(type);
|
||||
if (cppType.primitive) {
|
||||
decoderImplIncludes.add("presents/box/Boxed" + cppType.interpreter + ".h");
|
||||
}
|
||||
while (cppType != null) {
|
||||
if (cppType.representationImport != null) {
|
||||
receiverHeaderIncludes.add(cppType.representationImport);
|
||||
}
|
||||
cppType = cppType.dependent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.put("includes", decoderImplIncludes);
|
||||
CPPUtil.writeTemplate(DECODER_CPP_TMPL, _cpproot,
|
||||
CPPUtil.makePath(namespaces, dname, ".cpp"), ctx);
|
||||
|
||||
|
||||
ctx.put("includes", receiverHeaderIncludes);
|
||||
CPPUtil.writeTemplate(RECEIVER_HEADER_TMPL, _cpproot,
|
||||
CPPUtil.makePath(namespaces, rname, ".h"), ctx);
|
||||
|
||||
super.generateDecoder(receiver, source, rname, rpackage, methods, imports, rcode);
|
||||
}
|
||||
|
||||
protected File _cpproot;
|
||||
|
||||
protected static final String RECEIVER_HEADER_TMPL =
|
||||
"com/threerings/presents/tools/cpp/receiver_h.mustache";
|
||||
protected static final String DECODER_HEADER_TMPL =
|
||||
"com/threerings/corpse/tools/decoder_h.mustache";
|
||||
protected static final String DECODER_CPP_TMPL =
|
||||
"com/threerings/presents/tools/cpp/decoder_cpp.mustache";
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import com.threerings.presents.tools.GenServiceTask;
|
||||
|
||||
import static com.threerings.presents.tools.cpp.CPPUtil.makeNamespaces;
|
||||
import static com.threerings.presents.tools.cpp.CPPUtil.makePath;
|
||||
import static com.threerings.presents.tools.cpp.CPPUtil.writeTemplate;
|
||||
|
||||
public class GenCPPServiceTask extends GenServiceTask
|
||||
{
|
||||
public void setCpproot (File asroot)
|
||||
{
|
||||
_cpproot = asroot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateMarshaller (File source, ServiceDescription sdesc)
|
||||
throws Exception
|
||||
{
|
||||
String name = sdesc.sname.replace("Service", "Marshaller");
|
||||
String mpackage = sdesc.spackage.replace(".client", ".data");
|
||||
|
||||
List<String> namespaces = makeNamespaces(mpackage);
|
||||
Map<String, Object> ctx = Maps.newHashMap();
|
||||
ctx.put("name", name);
|
||||
ctx.put("javaName", mpackage + "." + name);
|
||||
ctx.put("namespaces", namespaces);
|
||||
ctx.put("namespace", Joiner.on("::").join(namespaces));
|
||||
ctx.put("methods", MethodDescriptor.from(sdesc.methods));
|
||||
ctx.put("listeners", sdesc.listeners);
|
||||
ctx.put("argbuilder", new CPPArgBuilder(true));
|
||||
|
||||
Set<String> includes = Sets.newHashSet();
|
||||
Set<String> implIncludes = Sets.newHashSet();
|
||||
for (ServiceMethod meth : sdesc.methods) {
|
||||
for (Type type : meth.method.getGenericParameterTypes()) {
|
||||
CPPType cppType = new CPPType(type);
|
||||
if (cppType.primitive) {
|
||||
implIncludes.add("presents/box/Boxed" + cppType.interpreter + ".h");
|
||||
}
|
||||
while (cppType != null) {
|
||||
if (cppType.representationImport != null) {
|
||||
includes.add(cppType.representationImport);
|
||||
}
|
||||
cppType = cppType.dependent;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.put("includes", implIncludes);
|
||||
writeTemplate(CPP_TMPL, _cpproot, makePath(namespaces, name, ".cpp"), ctx);
|
||||
ctx.put("includes", includes);
|
||||
writeTemplate(HEADER_TMPL, _cpproot, makePath(namespaces, name, ".h"), ctx);
|
||||
super.generateMarshaller(source, sdesc);
|
||||
}
|
||||
|
||||
protected File _cpproot;
|
||||
|
||||
protected static final String HEADER_TMPL =
|
||||
"com/threerings/presents/tools/cpp/marshaller_h.mustache";
|
||||
protected static final String CPP_TMPL =
|
||||
"com/threerings/presents/tools/cpp/marshaller_cpp.mustache";
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
import com.threerings.presents.net.Message;
|
||||
import com.threerings.presents.tools.GenTask;
|
||||
|
||||
import static com.threerings.presents.tools.cpp.CPPUtil.makeCPPName;
|
||||
import static com.threerings.presents.tools.cpp.CPPUtil.makeNamespaces;
|
||||
import static com.threerings.presents.tools.cpp.CPPUtil.makePath;
|
||||
|
||||
public class GenCPPStreamableTask extends GenTask
|
||||
{
|
||||
public class Generate {
|
||||
public void setClass (String name)
|
||||
{
|
||||
_toProcess.add(loadClass(name));
|
||||
}
|
||||
}
|
||||
|
||||
public Generate createGenerate ()
|
||||
{
|
||||
return new Generate();
|
||||
}
|
||||
|
||||
public void setCpproot (File asroot)
|
||||
{
|
||||
_cpproot = asroot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute ()
|
||||
{
|
||||
super.execute();
|
||||
for (Class<?> klass : _toProcess) {
|
||||
processClass(null, klass);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processClass (File fn, Class<?> sclass)
|
||||
{
|
||||
try {
|
||||
processClass(sclass);
|
||||
} catch (IOException e) {
|
||||
throw new BuildException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a resolved Streamable class instance.
|
||||
*/
|
||||
protected void processClass (Class<?> sclass)
|
||||
throws IOException
|
||||
{
|
||||
if (!Streamable.class.isAssignableFrom(sclass) ||
|
||||
((sclass.getModifiers() & Modifier.INTERFACE) != 0) ||
|
||||
DSet.class.equals(sclass) ||
|
||||
(InvocationMarshaller.class.isAssignableFrom(sclass) && !InvocationMarshaller.class.equals(sclass))) {
|
||||
// System.err.println("Skipping " + sclass.getName() + "...");
|
||||
return;
|
||||
}
|
||||
|
||||
System.err.println("Generating " + sclass.getName());
|
||||
|
||||
// see if our parent also implements Streamable
|
||||
boolean needSuper = Streamable.class.isAssignableFrom(sclass.getSuperclass()) &&
|
||||
!NONSUPER.contains(sclass.getSuperclass());
|
||||
|
||||
Map<String, Object> ctx = Maps.newHashMap();
|
||||
ctx.put("superclassStreamable", needSuper);
|
||||
ctx.put("name", sclass.getSimpleName());
|
||||
ctx.put("namespaces", makeNamespaces(sclass));
|
||||
ctx.put("javaName", sclass.getName());
|
||||
ctx.put("namespace", CPPUtil.makeNamespace(sclass));
|
||||
|
||||
Set<String> headerIncludes = Sets.newHashSet();
|
||||
Set<String> implIncludes = Sets.newHashSet();
|
||||
if (needSuper) {
|
||||
ctx.put("super", makeCPPName(sclass.getSuperclass()));
|
||||
addInclude(sclass.getSuperclass(), headerIncludes);
|
||||
} else {
|
||||
ctx.put("super", "Streamable");
|
||||
headerIncludes.add("presents/Streamable.h");
|
||||
}
|
||||
|
||||
List<CPPField> fields = Lists.newArrayList();
|
||||
ctx.put("fields", fields);
|
||||
for (Field field : sclass.getDeclaredFields()) {
|
||||
int mods = field.getModifiers();
|
||||
if (!Modifier.isStatic(mods) && !Modifier.isTransient(mods)) {
|
||||
CPPField cppField = new CPPField(field);
|
||||
fields.add(cppField);
|
||||
CPPType type = cppField.type;
|
||||
while (type != null) {
|
||||
if (CPPType.JAVA_LIST_FIXED.equals(type.fixed)) {
|
||||
implIncludes.add("presents/Streamer.h");
|
||||
}
|
||||
if (type.representationImport != null) {
|
||||
headerIncludes.add(type.representationImport);
|
||||
}
|
||||
type = type.dependent;
|
||||
}
|
||||
}
|
||||
}
|
||||
String inStream, outStream;
|
||||
if (fields.isEmpty() && !needSuper) {
|
||||
inStream = "/*in*/";
|
||||
outStream = "/*out*/";
|
||||
} else {
|
||||
inStream = "in";
|
||||
outStream = "out";
|
||||
|
||||
}
|
||||
ctx.put("inStreamArg", inStream);
|
||||
ctx.put("outStreamArg", outStream);
|
||||
|
||||
// now write all that out to the target source file
|
||||
ctx.put("includes", headerIncludes);
|
||||
CPPUtil.writeTemplate(HEADER_TMPL, _cpproot, makePath(sclass, ".h"), ctx);
|
||||
|
||||
ctx.put("includes", implIncludes);
|
||||
CPPUtil.writeTemplate(CPP_TMPL, _cpproot, makePath(sclass, ".cpp"), ctx);
|
||||
}
|
||||
|
||||
protected static void addInclude (Class<?> ftype, Set<String> includes)
|
||||
{
|
||||
includes.add(makePath(ftype, ".h"));
|
||||
}
|
||||
|
||||
protected File _cpproot;
|
||||
|
||||
protected Set<Class<?>> _toProcess = Sets.newHashSet();
|
||||
|
||||
protected static final Set<Class<?>> NONSUPER = Sets.newHashSet();
|
||||
static {
|
||||
NONSUPER.add(Message.class);
|
||||
NONSUPER.add(SimpleStreamableObject.class);
|
||||
}
|
||||
protected static final String HEADER_TMPL =
|
||||
"com/threerings/presents/tools/cpp/streamable_h.mustache";
|
||||
protected static final String CPP_TMPL =
|
||||
"com/threerings/presents/tools/cpp/streamable_cpp.mustache";
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.threerings.presents.tools.cpp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.threerings.presents.tools.InvocationTask.ServiceMethod;
|
||||
|
||||
public class MethodDescriptor
|
||||
{
|
||||
public static List<MethodDescriptor> from(List<ServiceMethod> methods) {
|
||||
return Lists.transform(methods, new Function<ServiceMethod, MethodDescriptor>() {
|
||||
@Override public MethodDescriptor apply (ServiceMethod from) {
|
||||
return new MethodDescriptor(from);
|
||||
}});
|
||||
}
|
||||
|
||||
public final String methodName;
|
||||
public final String vectorArguments;
|
||||
public final String arguments;
|
||||
public final String clientArguments;
|
||||
public final List<String> serviceArguments;
|
||||
|
||||
public MethodDescriptor(ServiceMethod methodSource) {
|
||||
methodName = methodSource.method.getName();
|
||||
vectorArguments = new CPPArgBuilder(false).getArgumentsFromVector(methodSource);
|
||||
arguments = new CPPArgBuilder(false).getArguments(methodSource);
|
||||
clientArguments = new CPPArgBuilder(true).getArguments(methodSource, "Shared<presents::PresentsClient> client");
|
||||
serviceArguments = new CPPArgBuilder(true).getServiceArguments(methodSource);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user