Add support for the Go programming language.
git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@12108 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
09f14197ac
commit
5af2978f77
259 changed files with 16159 additions and 14 deletions
16
Examples/go/class/Makefile
Normal file
16
Examples/go/class/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
TOP = ../..
|
||||
SWIG = $(TOP)/../preinst-swig
|
||||
CXXSRCS = example.cxx
|
||||
TARGET = example
|
||||
INTERFACE = example.i
|
||||
LIBS = -lm
|
||||
|
||||
all::
|
||||
$(MAKE) -f $(TOP)/Makefile CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \
|
||||
TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' go_cpp
|
||||
|
||||
clean::
|
||||
$(MAKE) -f $(TOP)/Makefile go_clean
|
||||
|
||||
check: all
|
||||
$(MAKE) -f $(TOP)/Makefile TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' go_run
|
||||
28
Examples/go/class/example.cxx
Normal file
28
Examples/go/class/example.cxx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* File : example.c */
|
||||
|
||||
#include "example.h"
|
||||
#define M_PI 3.14159265358979323846
|
||||
|
||||
/* Move the shape to a new location */
|
||||
void Shape::move(double dx, double dy) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
|
||||
int Shape::nshapes = 0;
|
||||
|
||||
double Circle::area(void) {
|
||||
return M_PI*radius*radius;
|
||||
}
|
||||
|
||||
double Circle::perimeter(void) {
|
||||
return 2*M_PI*radius;
|
||||
}
|
||||
|
||||
double Square::area(void) {
|
||||
return width*width;
|
||||
}
|
||||
|
||||
double Square::perimeter(void) {
|
||||
return 4*width;
|
||||
}
|
||||
284
Examples/go/class/example.go
Normal file
284
Examples/go/class/example.go
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 2.0.1
|
||||
*
|
||||
* This file is not intended to be easily readable and contains a number of
|
||||
* coding conventions designed to improve portability and efficiency. Do not make
|
||||
* changes to this file unless you know what you are doing--modify the SWIG
|
||||
* interface file instead.
|
||||
* ----------------------------------------------------------------------------- */
|
||||
|
||||
package example
|
||||
|
||||
|
||||
type _swig_fnptr *byte
|
||||
type _swig_memberptr *byte
|
||||
|
||||
|
||||
func _swig_allocatememory(int) *byte
|
||||
func _swig_internal_allocate(len int) *byte {
|
||||
return _swig_allocatememory(len)
|
||||
}
|
||||
|
||||
func _swig_allocatestring(*byte, int) string
|
||||
func _swig_internal_makegostring(p *byte, l int) string {
|
||||
return _swig_allocatestring(p, l)
|
||||
}
|
||||
|
||||
func _swig_internal_gopanic(p *byte, l int) {
|
||||
panic(_swig_allocatestring(p, l))
|
||||
}
|
||||
|
||||
type SwigcptrShape uintptr
|
||||
|
||||
func (p SwigcptrShape) Swigcptr() uintptr {
|
||||
return (uintptr)(p)
|
||||
}
|
||||
|
||||
func (p SwigcptrShape) SwigIsShape() {
|
||||
}
|
||||
|
||||
func _swig_wrap_delete_Shape(uintptr)
|
||||
|
||||
func DeleteShape(arg1 Shape) {
|
||||
_swig_wrap_delete_Shape(arg1.Swigcptr())
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_x_set(SwigcptrShape, float64)
|
||||
|
||||
func (arg1 SwigcptrShape) SetX(arg2 float64) {
|
||||
_swig_wrap_Shape_x_set(arg1, arg2)
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_x_get(SwigcptrShape) float64
|
||||
|
||||
func (arg1 SwigcptrShape) GetX() float64 {
|
||||
return _swig_wrap_Shape_x_get(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_y_set(SwigcptrShape, float64)
|
||||
|
||||
func (arg1 SwigcptrShape) SetY(arg2 float64) {
|
||||
_swig_wrap_Shape_y_set(arg1, arg2)
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_y_get(SwigcptrShape) float64
|
||||
|
||||
func (arg1 SwigcptrShape) GetY() float64 {
|
||||
return _swig_wrap_Shape_y_get(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_move(SwigcptrShape, float64, float64)
|
||||
|
||||
func (arg1 SwigcptrShape) Move(arg2 float64, arg3 float64) {
|
||||
_swig_wrap_Shape_move(arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_area(SwigcptrShape) float64
|
||||
|
||||
func (arg1 SwigcptrShape) Area() float64 {
|
||||
return _swig_wrap_Shape_area(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_perimeter(SwigcptrShape) float64
|
||||
|
||||
func (arg1 SwigcptrShape) Perimeter() float64 {
|
||||
return _swig_wrap_Shape_perimeter(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Shape_nshapes_set(int)
|
||||
|
||||
func SetShapeNshapes(arg1 int) {
|
||||
_swig_wrap_Shape_nshapes_set(arg1)
|
||||
}
|
||||
|
||||
func GetShapeNshapes() int
|
||||
type Shape interface {
|
||||
Swigcptr() uintptr
|
||||
SwigIsShape()
|
||||
SetX(arg2 float64)
|
||||
GetX() float64
|
||||
SetY(arg2 float64)
|
||||
GetY() float64
|
||||
Move(arg2 float64, arg3 float64)
|
||||
Area() float64
|
||||
Perimeter() float64
|
||||
}
|
||||
|
||||
type SwigcptrCircle uintptr
|
||||
|
||||
func (p SwigcptrCircle) Swigcptr() uintptr {
|
||||
return (uintptr)(p)
|
||||
}
|
||||
|
||||
func (p SwigcptrCircle) SwigIsCircle() {
|
||||
}
|
||||
|
||||
func _swig_wrap_new_Circle(float64) SwigcptrCircle
|
||||
|
||||
func NewCircle(arg1 float64) Circle {
|
||||
return _swig_wrap_new_Circle(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Circle_area(SwigcptrCircle) float64
|
||||
|
||||
func (arg1 SwigcptrCircle) Area() float64 {
|
||||
return _swig_wrap_Circle_area(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Circle_perimeter(SwigcptrCircle) float64
|
||||
|
||||
func (arg1 SwigcptrCircle) Perimeter() float64 {
|
||||
return _swig_wrap_Circle_perimeter(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_delete_Circle(uintptr)
|
||||
|
||||
func DeleteCircle(arg1 Circle) {
|
||||
_swig_wrap_delete_Circle(arg1.Swigcptr())
|
||||
}
|
||||
|
||||
func _swig_wrap_SetCircle_X(SwigcptrCircle, float64)
|
||||
|
||||
func (_swig_base SwigcptrCircle) SetX(arg1 float64) {
|
||||
_swig_wrap_SetCircle_X(_swig_base, arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_GetCircle_X(SwigcptrCircle) float64
|
||||
|
||||
func (_swig_base SwigcptrCircle) GetX() float64 {
|
||||
return _swig_wrap_GetCircle_X(_swig_base)
|
||||
}
|
||||
|
||||
func _swig_wrap_SetCircle_Y(SwigcptrCircle, float64)
|
||||
|
||||
func (_swig_base SwigcptrCircle) SetY(arg1 float64) {
|
||||
_swig_wrap_SetCircle_Y(_swig_base, arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_GetCircle_Y(SwigcptrCircle) float64
|
||||
|
||||
func (_swig_base SwigcptrCircle) GetY() float64 {
|
||||
return _swig_wrap_GetCircle_Y(_swig_base)
|
||||
}
|
||||
|
||||
func _swig_wrap_Circle_move(SwigcptrCircle, float64, float64)
|
||||
|
||||
func (_swig_base SwigcptrCircle) Move(arg1 float64, arg2 float64) {
|
||||
_swig_wrap_Circle_move(_swig_base, arg1, arg2)
|
||||
}
|
||||
|
||||
func (p SwigcptrCircle) SwigIsShape() {
|
||||
}
|
||||
|
||||
func (p SwigcptrCircle) SwigGetShape() Shape {
|
||||
return SwigcptrShape(p.Swigcptr())
|
||||
}
|
||||
|
||||
type Circle interface {
|
||||
Swigcptr() uintptr
|
||||
SwigIsCircle()
|
||||
Area() float64
|
||||
Perimeter() float64
|
||||
SetX(arg1 float64)
|
||||
GetX() float64
|
||||
SetY(arg1 float64)
|
||||
GetY() float64
|
||||
Move(arg1 float64, arg2 float64)
|
||||
SwigIsShape()
|
||||
SwigGetShape() Shape
|
||||
}
|
||||
|
||||
type SwigcptrSquare uintptr
|
||||
|
||||
func (p SwigcptrSquare) Swigcptr() uintptr {
|
||||
return (uintptr)(p)
|
||||
}
|
||||
|
||||
func (p SwigcptrSquare) SwigIsSquare() {
|
||||
}
|
||||
|
||||
func _swig_wrap_new_Square(float64) SwigcptrSquare
|
||||
|
||||
func NewSquare(arg1 float64) Square {
|
||||
return _swig_wrap_new_Square(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Square_area(SwigcptrSquare) float64
|
||||
|
||||
func (arg1 SwigcptrSquare) Area() float64 {
|
||||
return _swig_wrap_Square_area(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_Square_perimeter(SwigcptrSquare) float64
|
||||
|
||||
func (arg1 SwigcptrSquare) Perimeter() float64 {
|
||||
return _swig_wrap_Square_perimeter(arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_delete_Square(uintptr)
|
||||
|
||||
func DeleteSquare(arg1 Square) {
|
||||
_swig_wrap_delete_Square(arg1.Swigcptr())
|
||||
}
|
||||
|
||||
func _swig_wrap_SetSquare_X(SwigcptrSquare, float64)
|
||||
|
||||
func (_swig_base SwigcptrSquare) SetX(arg1 float64) {
|
||||
_swig_wrap_SetSquare_X(_swig_base, arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_GetSquare_X(SwigcptrSquare) float64
|
||||
|
||||
func (_swig_base SwigcptrSquare) GetX() float64 {
|
||||
return _swig_wrap_GetSquare_X(_swig_base)
|
||||
}
|
||||
|
||||
func _swig_wrap_SetSquare_Y(SwigcptrSquare, float64)
|
||||
|
||||
func (_swig_base SwigcptrSquare) SetY(arg1 float64) {
|
||||
_swig_wrap_SetSquare_Y(_swig_base, arg1)
|
||||
}
|
||||
|
||||
func _swig_wrap_GetSquare_Y(SwigcptrSquare) float64
|
||||
|
||||
func (_swig_base SwigcptrSquare) GetY() float64 {
|
||||
return _swig_wrap_GetSquare_Y(_swig_base)
|
||||
}
|
||||
|
||||
func _swig_wrap_Square_move(SwigcptrSquare, float64, float64)
|
||||
|
||||
func (_swig_base SwigcptrSquare) Move(arg1 float64, arg2 float64) {
|
||||
_swig_wrap_Square_move(_swig_base, arg1, arg2)
|
||||
}
|
||||
|
||||
func (p SwigcptrSquare) SwigIsShape() {
|
||||
}
|
||||
|
||||
func (p SwigcptrSquare) SwigGetShape() Shape {
|
||||
return SwigcptrShape(p.Swigcptr())
|
||||
}
|
||||
|
||||
type Square interface {
|
||||
Swigcptr() uintptr
|
||||
SwigIsSquare()
|
||||
Area() float64
|
||||
Perimeter() float64
|
||||
SetX(arg1 float64)
|
||||
GetX() float64
|
||||
SetY(arg1 float64)
|
||||
GetY() float64
|
||||
Move(arg1 float64, arg2 float64)
|
||||
SwigIsShape()
|
||||
SwigGetShape() Shape
|
||||
}
|
||||
|
||||
|
||||
type SwigcptrVoid uintptr
|
||||
type Void interface {
|
||||
Swigcptr() uintptr;
|
||||
}
|
||||
func (p SwigcptrVoid) Swigcptr() uintptr {
|
||||
return uintptr(p)
|
||||
}
|
||||
|
||||
39
Examples/go/class/example.h
Normal file
39
Examples/go/class/example.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* File : example.h */
|
||||
|
||||
class Shape {
|
||||
public:
|
||||
Shape() {
|
||||
nshapes++;
|
||||
}
|
||||
virtual ~Shape() {
|
||||
nshapes--;
|
||||
};
|
||||
double x, y;
|
||||
void move(double dx, double dy);
|
||||
virtual double area(void) = 0;
|
||||
virtual double perimeter(void) = 0;
|
||||
static int nshapes;
|
||||
};
|
||||
|
||||
class Circle : public Shape {
|
||||
private:
|
||||
double radius;
|
||||
public:
|
||||
Circle(double r) : radius(r) { };
|
||||
virtual double area(void);
|
||||
virtual double perimeter(void);
|
||||
};
|
||||
|
||||
class Square : public Shape {
|
||||
private:
|
||||
double width;
|
||||
public:
|
||||
Square(double w) : width(w) { };
|
||||
virtual double area(void);
|
||||
virtual double perimeter(void);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
10
Examples/go/class/example.i
Normal file
10
Examples/go/class/example.i
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* File : example.i */
|
||||
%module example
|
||||
|
||||
%{
|
||||
#include "example.h"
|
||||
%}
|
||||
|
||||
/* Let's just grab the original header file here */
|
||||
%include "example.h"
|
||||
|
||||
203
Examples/go/class/index.html
Normal file
203
Examples/go/class/index.html
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>SWIG:Examples:go:class</title>
|
||||
</head>
|
||||
|
||||
<body bgcolor="#ffffff">
|
||||
|
||||
|
||||
<tt>SWIG/Examples/go/class/</tt>
|
||||
<hr>
|
||||
|
||||
<H2>Wrapping a simple C++ class</H2>
|
||||
|
||||
<p>
|
||||
This example illustrates the most primitive form of C++ class wrapping
|
||||
performed by SWIG. In this case, C++ classes are simply transformed
|
||||
into a collection of C-style functions that provide access to class
|
||||
members.
|
||||
|
||||
<h2>The C++ Code</h2>
|
||||
|
||||
Suppose you have some C++ classes described by the following (and
|
||||
admittedly lame) header file:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
/* File : example.h */
|
||||
|
||||
class Shape {
|
||||
public:
|
||||
Shape() {
|
||||
nshapes++;
|
||||
}
|
||||
virtual ~Shape() {
|
||||
nshapes--;
|
||||
};
|
||||
double x, y;
|
||||
void move(double dx, double dy);
|
||||
virtual double area() = 0;
|
||||
virtual double perimeter() = 0;
|
||||
static int nshapes;
|
||||
};
|
||||
|
||||
class Circle : public Shape {
|
||||
private:
|
||||
double radius;
|
||||
public:
|
||||
Circle(double r) : radius(r) { };
|
||||
virtual double area();
|
||||
virtual double perimeter();
|
||||
};
|
||||
|
||||
class Square : public Shape {
|
||||
private:
|
||||
double width;
|
||||
public:
|
||||
Square(double w) : width(w) { };
|
||||
virtual double area();
|
||||
virtual double perimeter();
|
||||
};
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h2>The SWIG interface</h2>
|
||||
|
||||
A simple SWIG interface for this can be built by simply grabbing the
|
||||
header file like this:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
/* File : example.i */
|
||||
%module example
|
||||
|
||||
%{
|
||||
#include "example.h"
|
||||
%}
|
||||
|
||||
/* Let's just grab the original header file here */
|
||||
%include "example.h"
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
Note: when creating a C++ extension, you must run SWIG with
|
||||
the <tt>-c++</tt> option like this:
|
||||
<blockquote>
|
||||
<pre>
|
||||
% swig -c++ -go example.i
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h2>A sample Go script</h2>
|
||||
|
||||
See <a href="example.go">example.go</a> for a program that calls the
|
||||
C++ functions from Go.
|
||||
|
||||
<h2>Key points</h2>
|
||||
|
||||
<ul>
|
||||
<li>To create a new object, you call a constructor like this:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
c := example.NewCircle(10.0)
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
The name of the constructor is <tt>New</tt> followed by the name of
|
||||
the class, capitalized.
|
||||
|
||||
<p>
|
||||
<li>
|
||||
The constructor returns a value of interface type. The methods of the
|
||||
interface will be the methods of the C++ class, plus member accessor
|
||||
functions.
|
||||
|
||||
<p>
|
||||
<li>To access member data, a pair of accessor methods are used. For
|
||||
example:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
c.SetX(15) # Set member data
|
||||
x := c.GetX() # Get member data.
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
These are methods on the type returned by the constructor. The getter
|
||||
is named <tt>Get</tt> followed by the name of the member,
|
||||
capitalized. The setter is similar but uses <tt>Set</tt>.
|
||||
|
||||
<p>
|
||||
<li>To invoke a member function, you simply do this
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
fmt.Println("The area is", example.c.Area())
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<li>To invoke a destructor, simply do this
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
example.DeleteShape(c) # Deletes a shape
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
The name of the destructor is <tt>Delete</tt> followed by the name of
|
||||
the class, capitalized. (Note: destructors are currently not
|
||||
inherited. This might change later).
|
||||
|
||||
<p>
|
||||
<li>Static member variables are wrapped much like C global variables.
|
||||
For example:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
n := GetShapeNshapes() # Get a static data member
|
||||
SetShapeNshapes(13) # Set a static data member
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
The name is <tt>Get</tt> or <tt>Set</tt>, followed by the name of the
|
||||
class, capitalized, followed by the name of the member, capitalized.
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>General Comments</h2>
|
||||
|
||||
<ul>
|
||||
<li>This low-level interface is not the only way to handle C++ code.
|
||||
Director classes provide a much higher-level interface.
|
||||
|
||||
<p>
|
||||
<li>Because C++ and Go implement inheritance quite differently, you
|
||||
can not simply upcast an object in Go code when using multiple
|
||||
inheritance. When using only single inheritance, you can simply pass
|
||||
a class to a function expecting a parent class. When using multiple
|
||||
inheritance, you have to call an automatically generated getter
|
||||
function named <tt>Get</tt> followed by the capitalized name of the
|
||||
immediate parent. This will return the same object converted to the
|
||||
parent class.
|
||||
|
||||
<p>
|
||||
<li>
|
||||
Overloaded methods should normally work. However, when calling an
|
||||
overloaded method you must explicitly convert constants to the
|
||||
expected type when it is not <tt>int</tt> or <tt>float</tt>. In
|
||||
particular, a floating point constant will default to
|
||||
type <tt>float</tt>, but C++ functions typically expect the C++
|
||||
type <tt>double</tt> which is equivalent to the Go
|
||||
type <tt>float64</tt> So calling an overloaded method with a floating
|
||||
point constant typically requires an explicit conversion
|
||||
to <tt>float64</tt>.
|
||||
|
||||
<p>
|
||||
<li>Namespaces are not supported in any very coherent way.
|
||||
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
</body>
|
||||
</html>
|
||||
63
Examples/go/class/runme.go
Normal file
63
Examples/go/class/runme.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// This example illustrates how C++ classes can be used from Go using SWIG.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "./example"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// ----- Object creation -----
|
||||
|
||||
fmt.Println("Creating some objects:")
|
||||
c := NewCircle(10)
|
||||
fmt.Println(" Created circle", c)
|
||||
s := NewSquare(10)
|
||||
fmt.Println(" Created square", s)
|
||||
|
||||
// ----- Access a static member -----
|
||||
|
||||
fmt.Println("\nA total of", GetShapeNshapes(), "shapes were created")
|
||||
|
||||
// ----- Member data access -----
|
||||
|
||||
// Notice how we can do this using functions specific to
|
||||
// the 'Circle' class.
|
||||
c.SetX(20)
|
||||
c.SetY(30)
|
||||
|
||||
// Now use the same functions in the base class
|
||||
var shape Shape = s
|
||||
shape.SetX(-10)
|
||||
shape.SetY(5)
|
||||
|
||||
fmt.Println("\nHere is their current position:")
|
||||
fmt.Println(" Circle = (", c.GetX(), " ", c.GetY(), ")")
|
||||
fmt.Println(" Square = (", s.GetX(), " ", s.GetY(), ")")
|
||||
|
||||
// ----- Call some methods -----
|
||||
|
||||
fmt.Println("\nHere are some properties of the shapes:")
|
||||
shapes := []Shape{c, s}
|
||||
for i := 0; i < len(shapes); i++ {
|
||||
fmt.Println(" ", shapes[i])
|
||||
fmt.Println(" area = ", shapes[i].Area())
|
||||
fmt.Println(" perimeter = ", shapes[i].Perimeter())
|
||||
}
|
||||
|
||||
// Notice how the area() and perimeter() functions really
|
||||
// invoke the appropriate virtual method on each object.
|
||||
|
||||
// ----- Delete everything -----
|
||||
|
||||
fmt.Println("\nGuess I'll clean up now")
|
||||
|
||||
// Note: this invokes the virtual destructor
|
||||
// You could leave this to the garbage collector
|
||||
DeleteCircle(c)
|
||||
DeleteSquare(s)
|
||||
|
||||
fmt.Println(GetShapeNshapes(), " shapes remain")
|
||||
fmt.Println("Goodbye")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue