2012년 5월 8일 화요일

SWIG



wxWidgets 를 쓰려면, SWIG 를 써야할지도 모른다.

어쩌면, GO 보다 C++ 를 더 열심히 해야할지도 모른다.



http://www.swig.org/

SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages.

Support for the Go language was added in swig-2.0.1.



GUI

눈에 보이는것이 전부다.
손에 만질수 있는것이 전부다.

나는 cross-platform GUI library 를 찾고 있다.

GO 언어로 쓸수 있다면 더 좋을 것이다.





wxWidgets 에 포함된 wxAUI 를 통해 docking window 를 쓸수 있다고한다.







wxAUI is incorporated into the wxWidgets library





2012년 5월 6일 일요일

go languange



{ goroutine, channel, select } 의 조합은 무엇을 의미하는가.

이것은 { 데이터통신과 동기화 } 를 하나로 합쳐놓은것을 의미한다.

이것은 강력한 조합이다.

이 조합의 파급효과는 개발기간단축으로 나타날것이다.



struct, method, interface 조합은  무엇을 의미하는가.

개발이 장기간 진행되어 프로젝트의 파일수/라인수가 늘어나면
개발자가 필연적으로 마주치게되는,
class hierachy 의 복잡도가 증가하는 현상을
제거한다는것을 의미한다.
소스코드의 전체적인 의존성, 복잡도를 낮춘다는것을 의미한다.


C++ 에서 함수의 인자로 base class pointer 를 넘겨줄때나,
virtual function 을 써야할때,
GO 에서는 interface 를 함수의 인자로 넘기고,
interface 를 통해 함수를 호출하면,
마치 virtual function 과 같이 해당 type 의 method 가 호출된다.


디자인을 변경할때, 수정해야하는 코드의 양이 줄어든다.
디자인을 변경할때, 개발자가 머리속으로 처리해야하는 정보의 양이 줄어든다.
따라서 코드는 버그에대한 면역력이 높아지며, 개발기간은 단축된다.


쓰는 사람에게는 Script 언어가 주는 즐거움을 주는 이 언어는
놀랍게도 machine code 를 생성해낸다.
즉, 실행속도가 JAVA 나 .NET C# 보다 빠를것으로 예상된다.



다만, 아직은 성숙되지 않았다.
디버거와 통합된 쓸만한 IDE 가 몇년안에 제공되어야 한다.
UI 라이브러리를 포함한 여러가지 라이브러리가 오픈소스진영에서 쏟아져나와야 한다.

현재 Google App Engine 은 GO 언어를 지원한다고 한다.

Android 개발에도 GO 언어가 지원된다면.



2012년 5월 1일 화요일

channel

출처: http://tour.golang.org/#62


channel 한번 더보자.

channel 하나를 다수의 goroutine 에서 재사용이 가능하다는 얘기인가.

하나의 channel 은 사용이 완료될때까지 완전히 block 된다.

두개의 goroutine 에서 동시에 하나의 channel 을 쓸수는 없을것이다.



package main


import (
"fmt"
)


func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum
}


func main() {

a := []int {7, 2, 8, -9, 4, 0}

c:= make(chan int)

go sum( a[:len(a)/2], c)
go sum( a[len(a)/2:], c)


x, y := <-c, <-c

fmt.Println(x, y, x + y)
}




D:\DEV\PP\work_4\src>go run hello70.go
17 -5 12






error

출처: http://tour.golang.org/#54


error 를 한번 더 보자.

error 는 error string 이다.


error 는 interface 이다.

     type error interface {
         Error() string
     }



즉, Error() string 이란 메서드만 구현되어 있으면,
 모두 error 기능을 수행할 수 있다고 본다.

즉, 아래 run() 함수가 리턴하는 error 타입은
위에서 정의된 Error() string 메서드를 구현한 객체를 리턴한다는 얘기가 된다.





package main


import (
"fmt"
"time"
)


type MyError struct {
When time.Time
What string
}


func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s", e.When, e.What)
}


func run() error {
return &MyError{ time.Now(), "작동실패."}
}


func main() {
if err := run(); err != nil {
fmt.Println(err)
}
}






D:\DEV\PP\work_4\src>go run hello69.go
at 2012-05-01 11:09:15.2010718 +0000 UTC, 작동실패.




interface

출처: http://tour.golang.org/#52



interface 는 method 의 집합으로 표현한다.
interface type 변수에는 해당 method 를 가지고 있는 value 만 저장할 수 있다.

interface type 변수에 해당 함수객체나 함수포인터를 저장하는것이 아니다.
그 함수객체가 구현되어있는 변수값을 저장한다.


하나의 type 은 여러개의 interface 를 구현할 수 있다.
interface 때문에 C++ 의 상속, 객체 포인터 등을 쓸 필요가 없다.

함수를 정의할때, 파라메터로 객체의 타입을 지정하지 않고,
interface 타입을 지정하는 것을 눈여겨 보아야 한다.
GO 는 C++ 과는 다르다.


struct, method, interface.
이 세가지로 OOP 기능을 충분히 구현할 수 있다는 사실을 알 수 있다.
상속은 필요없다는 얘기다!
class heirachy 를 만들 필요가 없다.

GO 에서는 type, method 가 interface 로 느슨하게 연결되어 있다.

이것은 컴파일러가 암시적으로 표현된 코드로부터,
type, method 간의 의존관계를 자동적으로 처리해주기 때문에 가능한 일이다.

"연결관계를 상속을 통해서 엄격하게 표현하지 않고, 
                interface 를 통해서 느슨하게 표현함으로써,
       코드 변경작업(디자인변경작업)의 고통을 줄일수 있다"
는 사실을 그들은 알았던것이다.


하나의 type 이 여러개의 interface 를 구현할 수 있다는 말은,
C++ 의 다중상속 기능과 같은 기능을 발휘할 수 있다는 의미로 해석하기로 한다.



package main


import (
"fmt"
"math"
)


type Abser interface {
Abs() float64
}




func main() {
var a Abser
f := MyFloat( -math.Sqrt2)
v := Vertex{3, 4}

a = f
fmt.Println(a.Abs())

a = &v
// a = v
fmt.Println(a.Abs())
}


type MyFloat float64


func ( f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}


type Vertex struct {
X, Y float64
}


func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}






D:\DEV\PP\work_4\src>go run hello68.go
1.4142135623730951
5





struct 와 method


출처: http://tour.golang.org/#49


class 는 어디가고, struct 와 method 만 남았다.

method receiver 라는 용어가 등장했다. (v *Vertex)





package main


import (
"fmt"
"math"
)


type Vertex struct {
X, Y float64
}


func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}


func main() {
v := &Vertex{3, 4}
fmt.Println(v.Abs())
}




D:\DEV\PP\work_4\src>go run hello67.go
5





2012년 4월 30일 월요일

go language




지금은  GO 언어를 들여다 보고있다.

concurrency 는 속도가 목적이 아니라 디자인이 목적이라는 말.
기존 OOP 언어 개발방법에서 class hierachy 를 개발 초기에 결정하는 것이 거의 불가능하다는 점을 GO 는 만져준다.
concurrency + rpc 는 scalability 를 가능하게한다.
개발자의 주 업무는 디자인을 지속적으로 변경하는 일이라는것을 GO 언어는 염두에둔다.
지속적으로 디자인을 변경하는일이 짐이되지 않도록 하는 장치가 들어있는것으로 관찰된다.

GO1 은 아직 완성도가 높지않다.
지원하는 라이브러리가 적다. 특히 UI 가 아쉽다. 부족한 부분을 아직은 SWIG 에 의존하고 있는 상황이다.
빌드속도는 빠른것이 확인되었으나, 실행속도는 아직 확인되지 않았다.
최악의 경우 JAVA 정도의 속도가 될지도 모를일이라고, 나 혼자 걱정하고있다.


GO 에는 함수형 언어의 특성이 들어있다.
GO 에는 class 가 없다.
따라서, GO 는 C++ 개발자에게 디자인 패러다임을 전환할것을 요구한다.

사고의 전환은 개발자들의 시간과 에너지가 필요한일이다.
개발자들이 GO 디자인 패러다임에 적응하는데는 시간이 걸릴것이다.
그러나, 몇년안에 OOP 언어에 지친 개발자들은  GO 언어로 이동할것이다.
몇년안에 지금 부족한 라이브러리가 채워질것이다.


디자인변경이 고통스럽지 않아야한다.
thread, ipc, rpc, shared memory synchronization 기능을 언어차원에서 지원해야한다.
Scalability 가 가능해야한다.
여러가지 OS 에서 빌드할 수 있어야 한다.
실행속도가 빨라야 한다.
이것은 내가 원하는 언어의 요구사항이다.

메인언어로,
GO 언어가 C++ 언어의 대안이 될수 있을것인가.
그것을 앞으로 몇달간 살펴볼 예정이다.

goroutine 과 channel 을 쓸것인가 thread 와 mutex 를 쓸것인가.
그것을 앞으로 몇달간 살펴볼 예정이다.


결국, 나는 Better C++ 을 찾고 있는 중인가.



closure

출처: http://tour.golang.org/#37


모든 함수는 closure 이다.

C++ 문법에 closure 는 없었다.

closure 가 무엇인가.

코드실행결과를 보면, 함수내부의 변수값을 기억한다.

C++ 의 static 변수와 그 기능이 같다.

함수내부변수를 기억하는기능을 가진 함수. 그것이 closure 인가.




package main


import (
"fmt"
)


func main() {


fmt.Println("---------------------")

pos, neg := adder(), adder()
fmt.Printf( "%T\n", pos)

fmt.Println( pos(1))
fmt.Println( pos(1))
fmt.Println( pos(1))
fmt.Println( pos(1))
fmt.Println("---------------------")
fmt.Println( neg(-1))
fmt.Println( neg(-1))
fmt.Println( neg(-1))
fmt.Println( neg(-1))
fmt.Println("---------------------")
}


func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}



D:\DEV\PP\work_4\src>go run hello66.go
---------------------
func(int) int
1
2
3
4
---------------------
-1
-2
-3
-4
---------------------

Functions are values too.


출처: http://tour.golang.org/#36



함수는 값이다.








package main


import (
"fmt"
"math"
)




func main() {

hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}

fmt.Println("---------------------")
fmt.Println( hypot(3, 4))
fmt.Printf( "%T\n", hypot)
fmt.Println("---------------------")


}




D:\DEV\PP\work_4\src>go run hello65.go
---------------------
5
func(float64, float64) float64
---------------------




함수는 값이다. ( Functions are values too. )

런타임에 값의 타입을 정확히 가져올 수 있다는 말은,
Reflection 이 된다는 얘기다.



C++ 로만 말하던 나는,
Java 의 Reflection 이 부러웠다.
Python 의 Reflection 이 부러웠다.



rand

rand

난수는 의미있는 모든것의 근간이 됨을 우리는 알고있다.





package main


import (
"fmt"
"math/rand"
"time"
)




func main() {
seedNum := time.Now().UnixNano()
fmt.Println( "seed: ", seedNum)

rand.Seed( seedNum)

for i := 0; i < 5; i++ {
fmt.Println( rand.Int())
}

}





D:\DEV\PP\work_4\src>go run hello64.go
seed:  1335756904485079200
978468083
42468584
1238955245
1473912459
1318195603



gob

gob.

네트워크상으로 데이터를 전송할때나,
데이터를 파일에 저장할때 엔코딩/디코딩작업이 필요하다.

MFC 에서 Serialize 라는 용어로 표현되었던 그기능.

GO 에서는 gob 이 담당한다.

rpc package 는 gob 을 사용하여 구현되었다고 한다.





package main


import (
"bytes"
"encoding/gob"
"fmt"
"log"
)


type P struct {
X, Y, Z int
Name string
}


type Q struct {
X, Y *int32
Name string
}


func main() {


var network bytes.Buffer
enc := gob.NewEncoder(&network)
dec := gob.NewDecoder(&network)

err := enc.Encode(P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}

var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error:", err)
}

fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
}










error

error

error 를 한번 보자.





package main


import "fmt"
import "errors"


func main() {


if _, err := func1( -1); nil != err {
fmt.Println("not good:", err);
fmt.Printf("%T\n", err);
} else {
fmt.Println("good.");
}
}


func func1( f int) ( int, error) {
if f < 0 {
return 0, errors.New("여기서 음수는 에러입니다.")
}
return f, nil
}





D:\DEV\PP\work_4\src>go run hello61.go
not good: 여기서 음수는 에러입니다.
*errors.errorString













2012년 4월 29일 일요일

go chan select



goroutine, channel 과 함께 한 팀을 이루고있는
select 문을 한번 보자.


ch 에 데이터가 들어오기를 기다린다.
그런데, 계속기다리는것이 아니라, 일정시간동안(timeout )만 기다린다.

아래 코드에는 ch 에 데이터를 쓰는 부분이 없지만,
실제 코드 실행중에는 ch 에 데이터가 먼저들어올지,
timeout 이 먼저 발생할지, 알수없는 상황이라고 가정해보자.

사건발생의 임의성을 select 문으로 깔끔하게 묶어냈다.




package main


import "fmt"
import "time"


func main() {


ch := make( chan int)
timeout := make( chan bool, 1)


go func() {
time.Sleep( 1 * time.Second)
timeout <- true
} ()

select {
case <-ch:
fmt.Println("read from ch.")
case <-timeout:
fmt.Println("timeout.")
}


}


코드 출처: http://golang.org/doc/articles/concurrency_patterns.html






panic

panic.

프로그램을 불가피하게 멈춰야할때.
초기화 도중 불시착할때.

panic 은 가능한 안쓰는것이 좋을것이다.





package main


func main() {


func1()


}


func func1( ) {
panic("복구할수없는 에러.")
}







D:\DEV\PP\work_4\src>go run hello59.go
panic: 복구할수없는 에러.


goroutine 1 [running]:
main.func1()
        D:/DEV/PP/work_4/src/hello59.go:13 +0x4a
main.main()
        D:/DEV/PP/work_4/src/hello59.go:8 +0x1b


goroutine 2 [syscall]:
created by runtime.main
        C:/Users/ADMINI~1/AppData/Local/Temp/2/bindist119677522/go/src/pkg/runtime/proc.c:221
exit status 2


D:\DEV\PP\work_4\src>




parallel 은 아직 완전하지 않다.






package main
import "fmt"
import "runtime"
import "os"


func main() {


NCPU := runtime.NumCPU()
fmt.Println(NCPU)

runtime.GOMAXPROCS( NCPU)


maxcpu := os.Getenv("GOMAXPROCS")
fmt.Println( "[", maxcpu, "]")

}






channel


goroutine 이 종료되었을때, 종료보고기능을 위해서 channel 이 필요하다.

channel 은 make 함수를 사용하여 생성한다.

channel 은 통신기능은 물론, 동기화기능까지 제공한다.




나는 "종료보고기능"을 C++, C# 으로 구현해보려 했으나 만족할 만한 답을 찾지 못했다.
개인적인 견해로는, 통신 또는 데이터 공유와 동기화 기능이 하나로 합쳐져야 한다는 생각에는 전적으로 동의한다.
이런 기능이 뼈대로 제공되어야, 응용소프트웨어 분야에서 혁신이 나올 수 있다.






package main
import "fmt"


func main() {

c := make(chan int) // allocate a channel.

go func() {
fmt.Println(  "goroutine 3")
c <- 1 // send a signal; value does not matter
} ()

fmt.Println(  "output 3")

<-c // wait for finish; discard sent value
}






goroutine function literal


goroutine 을 정의할때,
function literal 을 쓰면 편리하다.




package main
import "fmt"
import "time"


func main() {

go func() {
fmt.Println(  "goroutine 2")
} ()

fmt.Println(  "output 2")


time.Sleep( 1000)
}










goroutine

goroutine.


thread 가 아니다. goroutine 이다.


키워드는 go 를 쓴다.






package main
import "fmt"
import "time"


func main() {

go fmt.Println(  "goroutine 1")

fmt.Println(  "output 1")


time.Sleep( 1000)
}














Embedding

Embedding



이것은 아직 이해하지 못했다.
나중에 와서 다시보자.












package main
import "fmt"


func main() {

fmt.Println(  "Embedding <-- subclassing")
}


type Reader interface {
Read( p []byte) (n int, err error)
}


type Writer interface {
Write( p []byte) (n int, err error)
}


type ReadWriter interface {
Reader
Writer
}



형변환 ( Conversion )

형변환 ( Conversion )

GO 언어에도 형변환기능이 있다.





package main
import "fmt"


func main() {

var n1 int = 1
var n2 float64

// error:
//  cannot use n1 (type int) as type float64 in assignment
// n2 = n1


n2 = float64( n1)

fmt.Println(  n2)
}








Interface

Interface

if something can do this, then it can be used here.

만약, Sequence type 이 Len(), Less(i, j int) bool, Swap(i, j int) 기능을 할 수 있다면,
그렇다면 Sequence type 은 sort.Sort() 함수호출에 사용할 수 있다.

Interface 는 어떤기능을 수행하기위한 요구사항, 필요조건을 기술하는 기능이다.

어떤기능을 서술하기위해, C++ 처럼 멤버함수형태로 기술한다면,
유사한 코드를 반복해서 작성해야하거나,
상속체계를 반복해서 수정해야하는 상황이 생길수 밖에 없다.

Interface 는 객체로부터 함수를 분리할 수 있게 해주는 장치로 이해하기로 한다.

함수 이름 앞에 붙어있는 "(s Sequence) funcName" 는
C++ 에서 "Sequence::funcName( Sequence & s)" 정도의 의미로 이해하기로 한다.


Interface 는 GO 언어의 주요한 뼈대에 해당한다는 사실을 느낄 수가 있다.

함수와 함수, 함수와 package, 코드와 프로그램 논리가 Interface 를 통해서 연결되는 것이리라고 예상해볼 수 있다.



package main
import "fmt"
import "sort"


func main() {

a := [...] int { 333, 222, 111 }

var b Sequence = a[0:3]

fmt.Println( "a:", a)
fmt.Println( "b:", b)

sort.Sort(b)

fmt.Println( "b:", b)
}


type Sequence []int


// Methods required by sort.Interface
func (s Sequence) Len() int {
return len(s)
}


func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}


func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}


func (s Sequence) String() string {
str := "["
for i, elem := range s {
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}





2012년 4월 28일 토요일

Method, Interface

이것은 아직 이해하지 못했다.
나중에 와서 다시보자.

Method, Interface








package main
import "fmt"


func main() { 

fmt.Println( "test")

var b ByteSlice


//fmt.Fprintf(&b, "%d", 7)
fmt.Fprintf(&b, "a")

fmt.Println( b)

}


type ByteSlice []byte




func (p *ByteSlice) Write( data []byte ) (n int, err error){


slice := *p

slice = data

*p = slice


return len(data), nil
}


func (p * ByteSlice) String() string {
var  str string = fmt.Sprint( *p)
return str
}







init()


소스파일은 자신의 초기화 함수를 정의할 수 있다.

클래스나 객체 단위가 아니다. "소스파일" 단위다.

init 함수는 호출하지 않아도 자동 호출된다.




Finally, each source file can define its own niladic init function






package main
import "fmt"


func main() { 
fmt.Println(  "main 함수 호출됨.")


fmt.Println(  "main 함수 종료됨.")
}


func init() {


fmt.Println(  "init 함수 호출됨.")

}





var

변수.




package main
import "fmt"
import "os"


func main() { 

var (
GOROOT = os.Getenv("GOROOT")
GOPATH = os.Getenv("GOPATH")
)

fmt.Printf( "%T\n", GOROOT)
fmt.Println(  GOROOT)
fmt.Println(  GOPATH)

}


enumerated constants

이것은 아직 이해하지 못했다.
나중에 와서 다시보자.



GO 언어에서 enumerated constants 는 iota enumerator 를 사용하여 만든다.




package main
import "fmt"


func main() { 

type ByteSize float64

const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
)

fmt.Printf( "%T\n", KB)
fmt.Println(  KB)
fmt.Println(  MB)
fmt.Println(  GB)
fmt.Println(  TB)

}



...


... 의 쓰임세 중에 이런것이 있다.

한번 쳐다봐 주자.

append 함수도 한번 쳐다봐 주자.





package main
import "fmt"


func main() { 


a := [...] int { 1, 2, 3}
x := [] int { 1, 2, 3}

fmt.Printf( "type-a: %T\n",  a)
fmt.Printf( "type-x: %T\n",  x)

fmt.Println(  x)

x = append( x, 4, 5, 6)
fmt.Println(  x)

y := [] int { 7, 8, 9 }
x = append( x, y... )
fmt.Println(  x)

}






가변 파라메터

함수에 넘겨주는 parameter 가 여러개일때,
... parameter 를 쓰는 모양새를 쳐다보자.




package main
import "fmt"


func main() { 


fmt.Println(  Min( 41, 32, 23, 14, 75))

}




func Min( a ... int ) int {
min := int( ^uint(0) >> 1) // largest int
for _, i := range a {
if i < min {
min = i
}
}
return min
}




Print format

Print 에서 format

custom type 에대한 format 을 control 하려면,
String() string 함수를 만들면 된다.


%T 는 type of value 를 출력한다.





package main
import "fmt"


func main() { 


var timeZone = map[string] int {
"UTC" : 1,
"EST" : 2,
"CST" : 3,
"MSt" : 4,
"PST" : 5,
}



t := &T{ 7, -2.35, "abc"}
var str string = "abc"

fmt.Printf( "%v \n", t)
fmt.Printf( "%+v \n", t)
fmt.Printf( "%#v \n", t)
fmt.Printf( "%#v \n", timeZone)
fmt.Printf( "%q \n", str)
fmt.Printf( "%#q \n", str)
fmt.Printf( "%x \n", str)
fmt.Printf( "% x \n", str)
fmt.Printf( "%T \n", str)
fmt.Printf( "%T \n", timeZone)

}




type T struct {
a int
b float64
c string
}




func (t *T) String() string {
return fmt.Sprintf( "custom control: %d/%g/%q", t.a, t.b, t.c)
}