BLOG main image
Category (326)
News (16)
All about me (1)
Diary (1)
Projects (8)
Programming (95)
Ideas (8)
Treasures (28)
Study (59)
Bookmark (19)
iPhone (77)
만들어보자!! Game Engine fo.. (0)
Android (0)
The matter of a single trade c..
17:23 - Ken Griffey Jr Shoes
The matter of a single trade c..
17:23 - Ken Griffey Jr Shoes
The matter of a single trade c..
17:22 - Ken Griffey Jr Shoes
Concentration and perseverance..
17:19 - Ken Griffey Jr Shoes
http://www.replicaoakleysungla..
05/18 - hgfhg
thanks for sharing
05/16 - replica watches
For Coach handbags alone, eBay..
05/10 - Coach outlet
thanks for sharing
05/10 - Coach outlet
좋은 게시물, 공유를위한 감사합..
05/10 - china wholesale
All of us need to preserve up..
05/07 - replica watches
free microsoft office 2010
free microsoft office 2010
特殊網站設計
特殊網站設計
特殊網站設計
特殊網站設計
Business Idea
Business Idea
surf lessons newport beach
surf lessons newport beach
295,709 Visitors up to today!
Today 2 hit, Yesterday 192 hit
daisy rss
tistory 티스토리 가입하기!
'Programming/Tips'에 해당되는 글 48건
2010/06/15 13:40
Possible freebie options for game designers on a budget:
Gimp for raster graphics.
Inkscape for vector graphics.

These are the two that I use for the most part. Note that you can also create 2D game art by creating 3D models in Blender then rendering. This is how I make most of my UI stuff.

Trackback Address :: http://joyholic.kr/trackback/415 관련글 쓰기
Name
Password
Homepage
Secret
2010/05/31 09:45
출처를 모르겠습니다. 저도 메일로 받은거라..
혹시, 문제가 되면, 바로 지우도록 하겠습니다. 


Feature creep이라는 말이 있습니다. 게임 개발 뿐만 아니라 많은 소프트웨어 개발 프로젝트에서 발견할 수 있는 것들입니다.

 

마침 위키피디아에서 feature creep을 잘 설명한 글이 있습니다. (링크) 요약해보죠.

  • 제품을 개발하다보면 사용자들이 원하는 기능과 쓸모있는 기능들을 넣고자 하는 열망이 생긴다. 그리고 그렇게 함으로 매출을 올리고자 한다아이러니하게도, 이것이 feature creep의 시작이다.
  • feature creep이 지속되면 불필요한 기능들이 자꾸만 들어가게 되고 프로젝트의 복잡도가 증가하며 제품의 특장점과 핵심 기능이 서서히 감춰지게 된다.
  • feature creep은 이미 개발된 (혹은 개발중인) 프로젝트를 유지하려는 의지가 원인이 되기도 한다.
  • feature creep은 프로젝트의 개발 비용을 과다하게 증가시킬 뿐만 아니라 프로젝트를 죽여버리는 원인이 되기도 한다.

Feature creep으로 진화된 물건바지 주머니에 넣으려면?

 

feature creep 매우 흔합니다당장에 여러분이 참여하고 있는 프로젝트에서도 발견할지도 모릅니다.

 

feature creep을 일으킬 수 있는 위험인자는 흔하게 널려있습니다. 여러분의 직장 상사, 여러분의 친인척들, 심지어 여러분 자신 및 여러분이 지금 열심히 보고 있는 책에도 있을 수 있습니다저 또한 많은 feature creep에서 고생해본 사람이기도 합니다.

 

어떤 경우에서 생기는지 보도록 하죠.

 

프로그래머들은 신기술이 나오면 바로 적용해보고 싶어합니다. 뭔가 엘레강스한 대단한 아키텍처(나중에 알고보니 안티패턴임을 뒤늦게 깨달을)를 적용해보고 싶어합니다어느날모노리스 코드가 만들어지고 심지어 자기 스스로도 감당하기 힘든 복잡도를 가진 괴물을 만들고 맙니다. 그리고 회사가 개발 일정과 (자금이) 나빠지면서 슬슬 구인 게시판을 뒤져보기 시작합니다. 중요한 실력과 정교한 개발보다 많은 시도를 해보고 싶어하는 (나쁘게 말해서 회사를 상대로 실험해보려는모험심 강한 (그러나 실력과 책임감이 그만큼 받쳐지지 못하는프로그래머에게서 볼 수 있습니다.

 

게임 기획자들은 대단한 게임을 만들고 싶은 마음에 여러가지 아이디어들을 계속 집어넣습니다. 다른 게임에서 감동한 기능들을 자꾸만 넣습니다. 뭔가 엘레강스한 완성도의 게임을 꿈꾸며 대량의 기획서를 작성합니다. 그리고 플레이를 해보니 별로 재미가 없습니다안되겠습니다. 뭔가를 추가해야겠다고 생각합니다. 장르도 늘어납니다. 처음에는 액션 게임이었는데 만들다보니 MMO + RPG + 전략시뮬 + 커뮤니티 + 어드벤처 + 음악댄스 + 영어교육 게임이 되어버립니다.

 

경영진,마케터,운영팀도 feature creep에 일조합니다. 유저들의 피드백, 시장 현황, 소문 등은 개발 방향에 자꾸만 쓸데없는 암세포를 붙이게 합니다.

 

횟집은 뭐니뭐니해도 회가 신선하고 맛있어야 합니다스끼다시와 매운탕만 잔뜩 줘봤자 회가 물러터졌으면 소용없죠. Feature Creep은 동네 음식점에서도 흔히 볼 수 있습니다.

 

feature creep으로 고생하는 게임 개발 프로젝트의 공통점은 다음과 같습니다.

  • 프로젝트 초기에 만들려고 했던 것이 분명하게 구체화되어있지 않았습니다. 아무것도 구체화되어있지 않으면서 정작 만들고자 하는 것은 거대한 꿈 덩어리입니다.
  • 현실 감각을 잊은 지나친 열정을 갖고 있습니다. 단 한개의 궁극 완성도 제품을 만들려는 열망만 가득합니다현실적으로, 신차를 개발해도 포니부터 시작해야 소나타도 만들고 그랜저도 만들고 궁극적으로 에쿠스도 만드는건데, 처음부터 페라리를 만들려고 합니다. 즉 프로젝트 범위의 상한선을 미리 그어놓지 않았습니다.
  • 디렉터가 중간 결과물이 별로 마음에 들지 않는다는 이유로 당황합니다. 그리고 돌파구를 "더 멋진 시스템의 추가"에서 찾으려만 합니다. 그냥 버리고 완전히 새로운 것을 시작하는게 더 나을 것 같은데 말이죠.

단도직입적으로 말하죠. Feature creep은 가장 쉬우면서 무능력한 개발 방향입니다

 

Feature creep "자꾸만 넣는다"인데 이에 반대되는 말은 "더 이상 뺄 것이 없을 때까지 빼기"입니다. 그리고 더 이상 뺄 것이 없을 때까지 빼버리고, 남은 것들에 대해서는 최고의 품질을 만들기 위해 집중을 한 제품 중에서는 시장에서 성공한 것이 많습니다제가 생각하는 이러한 예는 비주얼드, 광란의 수족관, 팡야(골프게임), 아이팟입니다. ProudNet을 쓰는 프로젝트 중 몇 개도 있고요.

 

혹시 여러분의 프로젝트에서는 알게 모르게 feature creep이 범해지고 있지 않나요?

Trackback Address :: http://joyholic.kr/trackback/401 관련글 쓰기
Name
Password
Homepage
Secret
2009/03/09 16:37
Original : http://en.wikipedia.org/wiki/Name_mangling

Name mangling

From Wikipedia, the free encyclopedia

Jump to: navigation, search

In software compiler engineering, name mangling (more properly called name decoration, although this term is less commonly used) is a technique used to solve various problems caused by the need to resolve unique names for programming entities in many modern programming languages.

It provides a way of encoding additional information about the name of a function, structure, class or another datatype in order to pass more semantic information from the compilers to linkers.

The need arises where the language allows different entities to be named with the same identifier as long as they occupy a different namespace (where a namespace is typically defined by a module, class, or explicit namespace directive).

Any object code produced by compilers is usually linked with other pieces of object code (produced by the same or another compiler) by a type of program called a linker. The linker needs a great deal of information on each program entity. For example, to correctly link a function it needs its name, the number of arguments and their types, and so on.

Contents

[hide]

[edit] C name decoration in Microsoft Windows

Although name mangling is not generally required or used by languages that do not support function overloading (such as C and classic Pascal), they use it in some cases to provide additional information about a function. For example, compilers targeted at Microsoft Windows platforms support a variety of calling conventions, which determine the manner in which parameters are sent to subroutines and results returned. Because the different calling conventions are not compatible with one another, compilers mangle symbols with codes detailing which convention should be used.

The mangling scheme was established by Microsoft, and has been informally followed by other compilers including Digital Mars, Borland, and GNU gcc. The scheme even applies to other languages, such as Pascal, D, Delphi, Fortran, and C#. This allows subroutines written in those languages to call, or be called by, existing Windows libraries using a calling convention different from their default.

When compiling the following C examples:

int _cdecl    f (int x) { return 0; }
int _stdcall  g (int y) { return 0; }
int _fastcall h (int z) { return 0; }

_cdecl is the default for C functions, if no calling convention is stated explicitly.

32 bit compilers emit, respectively:

_f
_g@4
@h@4

In the stdcall and fastcall mangling schemes, the function is encoded as _name@X and @name@X, for stdcall and fastcall respectively, where X is the number of bytes, in decimal, of the argument(s) in the parameter list (including those passed in registers, for fastcall).

Other common name decoration actions may involve adding prefixes, usually with an abundance of underscores (like __func__), or some standard capitalization.


[edit] Name mangling in C++

C++ compilers are the most widespread, and yet least standard, users of name mangling. The first C++ compilers were implemented as translators to C source code, which would then be compiled by a C compiler to object code; because of this, symbol names had to conform to C identifier rules. Even later, with the emergence of compilers which produced machine code or assembly directly, the system's linker generally did not support C++ symbols, and mangling was still required.

The C++ language does not define a standard decoration scheme, so each compiler uses its own. Combined with the fact that C++ decoration can become fairly complex (storing information about classes, templates, namespaces, operator overloading, etc), this means that object code produced by different compilers is not usually linkable.

[edit] Simple example

Consider the following two definitions of f() in a C++ program:

int  f (void) { return 1; }
int  f (int)  { return 0; }
void g (void) { int i = f(), j = f(0); }

These are distinct functions, with no relation to each other apart from the name. If they were natively translated into C with no changes, the result would be an error — C does not permit two functions with the same name. The compiler therefore will encode the type information in the symbol name, the result being something resembling:

int  __f_v (void) { return 1; }
int  __f_i (int)  { return 0; }
void __g_v (void) { int i = __f_v(), j = __f_i(0); }

Notice that g() is mangled even though there is no conflict; name mangling applies to all symbols.

[edit] Complex example

For a more complex example, we'll consider an example of a real-world name mangling implementation: that used by GNU GCC 3.x, and how it mangles the following example class. The mangled symbol is shown below the respective identifier name.

namespace wikipedia 
{
   class article 
   {
   public:
      std::string format (void); 
         /* = _ZN9wikipedia7article6formatEv */
 
      bool print_to (std::ostream&); 
         /* = _ZN9wikipedia7article8print_toERSo */
 
      class wikilink 
      {
      public:
         wikilink (std::string const& name);
            /* = _ZN9wikipedia7article8wikilinkC1ERKSs */
      };
   };
}

The name mangling scheme used here is relatively simple. All mangled symbols begin with _Z (note that an underscore followed by a capital is a reserved identifier in C and C++, so conflict with user identifiers is avoided); for nested names (including both namespaces and classes), this is followed by N, then a series of <length, id> pairs (the length being the length of the next identifier), and finally E. For example, wikipedia::article::format becomes

_ZN·9wikipedia·7article·6format·E  

For functions, this is then followed by the type information; as format() is a void function, this is simply v; hence:

_ZN·9wikipedia·7article·6format·E·v

For print_to, a standard type std::ostream (or more properly std::basic_ostream<char, char_traits<char> >) is used, which has the special alias So; a reference to this type is therefore RSo, with the complete name for the function being:

_ZN·9wikipedia·7article·8print_to·E·RSo

[edit] How different compilers mangle the same functions

There isn't a standard scheme by which even trivial C++ identifiers are mangled, and consequently different compiler vendors (or even different versions of the same compiler, or the same compiler on different platforms) mangle public symbols in radically different (and thus totally incompatible) ways. Consider how different C++ compilers mangle the same functions:

Compiler void h(int) void h (int, char) void h(void)
GNU GCC 3.x _Z1hi _Z1hic _Z1hv
GNU GCC 2.9x h__Fi h__Fic h__Fv
Intel C++ 8.0 for Linux _Z1hi _Z1hic _Z1hv
Microsoft VC++ v6/v7 ?h@@YAXH@Z ?h@@YAXHD@Z ?h@@YAXXZ
Borland C++ v3.1 @h$qi @h$qizc @h$qv
OpenVMS C++ V6.5 (ARM mode) H__XI H__XIC H__XV
OpenVMS C++ V6.5 (ANSI mode) CXX$__7H__FI0ARG51T CXX$__7H__FIC26CDH77 CXX$__7H__FV2CB06E8
OpenVMS C++ X7.1 IA-64 CXX$_Z1HI2DSQ26A CXX$_Z1HIC2NP3LI4 CXX$_Z1HV0BCA19V
Digital Mars C++ ?h@@YAXH@Z ?h@@YAXHD@Z ?h@@YAXXZ
SunPro CC __1cBh6Fi_v_ __1cBh6Fic_v_ __1cBh6F_v_
HP aC++ A.05.55 IA-64 _Z1hi _Z1hic _Z1hv
HP aC++ A.03.45 PA-RISC h__Fi h__Fic h__Fv
Tru64 C++ V6.5 (ARM mode) h__Xi h__Xic h__Xv
Tru64 C++ V6.5 (ANSI mode) __7h__Fi __7h__Fic __7h__Fv

Notes:

  • The Compaq C++ compiler on OpenVMS VAX and Alpha (but not IA-64) and Tru64 has two name mangling schemes. The original, pre-standard scheme is known as ARM model, and is based on the name mangling described in the C++ Annotated Reference Manual (ARM). With the advent of new features in standard C++, particularly templates, the ARM scheme became more and more unsuitable — it could not encode certain function types, or produced identical mangled names for different functions. It was therefore replaced by the newer "ANSI" model, which supported all ANSI template features, but was not backwards compatible.
  • On IA-64, a standard ABI exists (see external links), which defines (among other things) a standard name-mangling scheme, and which is used by all the IA-64 compilers. GNU GCC 3.x, in addition, has adopted the name mangling scheme defined in this standard for use on other, non-Intel platforms.

[edit] Handling of C symbols when linking from C++

The job of the common C++ idiom:

#ifdef __cplusplus 
extern "C" {
#endif
    /* ... */
#ifdef __cplusplus
}
#endif

is to ensure that the symbols following are "unmangled" – that the compiler emits a binary file with their names undecorated, as a C compiler would do. As C language definitions are unmangled, the C++ compiler needs to avoid mangling references to these identifiers.

For example, the standard strings library, <string.h> usually contains something resembling:

#ifdef __cplusplus
extern "C" {
#endif
 
void *memset (void *, int, size_t);
char *strcat (char *, const char *);
int   strcmp (const char *, const char *);
char *strcpy (char *, const char *);
 
#ifdef __cplusplus
}
#endif

Thus, code such as:

if (strcmp(argv[1], "-x") == 0) 
    strcpy(a, argv[2]);
else 
    memset (a, 0, sizeof(a));

uses the correct, unmangled strcmp and memset. If the extern had not been used, the C++ compiler would produce code equivalent to:

if (__1cGstrcmp6Fpkc1_i_(argv[1], "-x") == 0) 
    __1cGstrcpy6Fpcpkc_0_(a, argv[2]);
else 
    __1cGmemset6FpviI_0_ (a, 0, sizeof(a));

Since those symbols do not exist in the C runtime library (e.g. libc), link errors would result.


[edit] Standardised name mangling in C++

While it is a relatively common belief that standardised name mangling in the C++ language would lead to greater interoperability between implementations, this is not really the case. Name mangling is only one of several application binary interface issues in a C++ implementation. Other ABI issues like exception handling, virtual table layout, structure padding, etc. cause differing C++ implementations to be incompatible. Further, requiring a particular form of mangling would cause issues for systems where implementation limits (e.g. length of symbols) dictate a particular mangling scheme. A standardised requirement for name mangling would also prevent an implementation where mangling was not required at all — for example, a linker which understood the C++ language.

The C++ standard therefore does not attempt to standardise name mangling. On the contrary, the Annotated C++ Reference Manual (also known as ARM, ISBN 0-201-51459-1, section 7.2.1c) actively encourages the use of different mangling schemes to prevent linking when other aspects of the ABI, such as exception handling and virtual table layout, are incompatible.

[edit] Real-world effects of C++ name mangling

Because C++ symbols are routinely exported from DLL and shared object files, the name mangling scheme is not merely a compiler-internal matter. Different compilers (or different versions of the same compiler, in many cases) produce such binaries under different name decoration schemes, meaning that symbols are frequently unresolved if the compilers used to create the library and the program using it employed different schemes. For example, if a system with multiple C++ compilers installed (e.g. GNU GCC and the OS vendor's compiler) wished to install the Boost library, it would have to be compiled twice — once for the vendor compiler and once for GCC.

It is good for safety purposes that compilers producing incompatible object codes (codes based on different ABIs, regarding e.g. classes and exceptions) use different name mangling schemes. This guarantees that these incompatibilities are detected at the linking phase, not when executing the software.

For this reason name decoration is an important aspect of any C++-related ABI.

[edit] Name mangling in Java

The language, compiler, and .class file format were all designed together (and had object-orientation in mind from the start), so the primary problem solved by name mangling doesn't exist in implementations of the Java runtime. There are, however, cases where an analogous transformation and qualification of names is necessary.

[edit] Creating unique names for inner and anonymous classes

The scope of anonymous classes is confined to their parent class, so the compiler must produce a "qualified" public name for the inner class, to avoid conflict where other classes (inner or not) exist in the same namespace. Similarly, anonymous classes must have "fake" public names generated for them (as the concept of anonymous classes exists only in the compiler, not the runtime). So, compiling the following java program

public class foo {
    class bar {
        public int x;
    }
 
    public void zark () {
        Object f = new Object () {
            public String toString() {
                return "hello";
            }
        };
    }
}

will produce three .class files:

  • foo.class, containing the main (outer) class foo
  • foo$bar.class, containing the named inner class foo.bar
  • foo$1.class, containing the anonymous inner class (local to method foo.zark)

All of these class names are valid (as $ symbols are permitted in the JVM specification) and these names are "safe" for the compiler to generate, as the Java language definition prohibits $ symbols in normal java class definitions.

Name resolution in Java is further complicated at runtime, as fully qualified class names are unique only inside a specific classloader instance. Classloaders are ordered hierarchically and each Thread in the JVM has a so called context class loader, so in cases where two different classloader instances contain classes with the same name, the system first tries to load the class using the root (or system) classloader and then goes down the hierarchy to the context class loader.

[edit] Handling issues with the java to native interface

Java's native method support allows java language programs to call out to programs written in another language (generally either C or C++). There are two name-resolution concerns here, neither of which is implemented in a particularly standard manner.

[edit] Name mangling in Python

A Python programmer can explicitly designate that an identifier is a "private name" (its scope is confined to the class) by setting the first two characters of the identifier to be underscores. Mangling will not be performed if the identifier ends with more than one underscore, for example, __thing will be mangled, as will ___thing and __thing_, but __thing__ and __thing___ will not. On encountering these, the Python compiler or interpreter turns these private names into global symbols by prepending a string consisting of a single underscore and the name of the enclosing class.

So, for example,

class Test:
    def __private_symbol(self):
        pass
    def normal_symbol(self):
        pass
 
print dir(Test)

will output:

['_Test__private_symbol', 
'__doc__', 
'__module__', 
'normal_symbol']

[edit] Name mangling in Borland's Turbo Pascal / Delphi range

To avoid name mangling in Pascal, use:

exports
  myFunc name 'myFunc', myProc name 'myProc';

[edit] Name mangling in Objective-C

Essentially two forms of method exist in Objective-C, the class ("static") method, and the instance method. A method declaration in Objective-C is of the following form

+ method name: argument name1:parameter1 ...
- method name: argument name1:parameter1 ...

Class methods are signified by +, instance methods use -. A typical class method declaration may then look like:

+ (id) initWithX: (int) number andY: (int) number;
+ (id) new;

with instance methods looking like

- (id) value;
- (id) setValue: (id) new_value;

Each of these method declarations have a specific internal representation. When compiled, each method is named according to the following scheme for class methods:

_c_Class_methodname_name1_name2_ ...

and this for instance methods:

_i_Class_methodname_name1_name2_ ...

The colons in the Objective-C syntax are translated to underscores. So, the Objective-C class method + (id) initWithX: (int) number andY: (int) number;, if belonging to the Point class would translate as _c_Point_initWithX_andY_, and the instance method (belonging to the same class) - (id) value; would translate to _i_Point_value.

Each of the methods of a class are labeled in this way. However, in order to look up a method that a class may respond to would be tedious if all methods are represented in this fashion. Each of the methods is assigned a unique symbol (such as an integer). Such a symbol is known as a selector. In Objective-C, one can manage selectors directly — they have a specific type in Objective-C — SEL.

During compilation, a table is built that maps the textual representation (such as _i_Point_value) to selectors (which are given a type SEL). Managing selectors is more efficient than manipulating the textual representation of a method. Note that a selector only matches a method's name, not the class it belongs to — different classes can have different implementations of a method with the same name. Because of this, implementations of a method are given a specific identifier too — these are known as implementation pointers, and are given a type also, IMP.

Message sends are encoded by the compiler as calls to the id objc_msgSend (id receiver, SEL selector, ...) function, or one of its cousins, where receiver is the receiver of the message, and SEL determines the method to call. Each class has its own table that maps selectors to their implementations — the implementation pointer specifies where in memory the actual implementation of the method resides. There are separate tables for class and instance methods. Apart from being stored in the SEL to IMP lookup tables, the functions are essentially anonymous.

The SEL value for a selector does not vary between classes. This enables polymorphism.

The Objective-C runtime maintains information about the argument and return types of methods. However, this information is not part of the name of the method, and can vary from class to class.

Since Objective-C does not support namespaces, there is no need for mangling of class names (that do appear as symbols in generated binaries).

[edit] Name mangling in Fortran

Name mangling is also necessary in Fortran compilers, originally because the language is case insensitive. Further mangling requirements were imposed later in the evolution of the language because of the addition of modules and other features in the Fortran 90 standard. The case mangling, especially, is a common issue that must be dealt with in order to call Fortran libraries (such as LAPACK) from other languages (such as C).

Because of the case insensitivity, the name of a subroutine or function "FOO" must be converted to a canonical case and format by the Fortran compiler so that it will be linked in the same way regardless of case. Different compilers have implemented this in various ways, and no standardization has occurred. The AIX and HP-UX Fortran compilers convert all identifiers to lower case ("foo"), while the Cray Unicos Fortran compilers converted identifiers all upper case ("FOO"). The GNU g77 compiler converts identifiers to lower case plus an underscore ("foo_"), except that identifiers already containing an underscore ("FOO_BAR") has two underscores appended ("foo_bar__"), following a convention established by f2c. Many other compilers, including SGI's IRIX compilers, gfortran, and Intel's Fortran compiler, convert all identifiers to lower case plus an underscore ("foo_" and "foo_bar_").

Identifiers in Fortran 90 modules must be further mangled, because the same subroutine name may apply to different routines in different modules.

[edit] External links

Trackback Address :: http://joyholic.kr/trackback/335 관련글 쓰기
Name
Password
Homepage
Secret
2009/03/09 12:57
출처 : http://jinmoda.tistory.com/622

네트워크로 연결된 두대(혹은 여러대)의 컴퓨터에서 마우스와 키보드를 공유하는 아주 유용한 유틸리티입니다.

서버와 클라이언트에 각각 시너지 프로그램을 설치하고 설정만 해주면 마우스와 키보드를 공유할수있습니다.
서버 : 컨트롤하는 관리자의 컴퓨터, 클라이언트 : 컨트롤 당하는 고객의 컴퓨터라고 생각하시면 됩니다.
양 컴퓨터간 클립보드도 지원하며, 파일전송, 한영키변환이 안되는게 좀 불편합니다.

파일공유 및 클립보드(서버에서 한글복사, 클라이언트에서 붙여넣기)로 해결은 가능하지만 자주사용하다보면 불편함을 많이 느낄수가 있습니다.

참고로 Vista에서 접속이 잘안되거나 특정 프로그램에서 오류가 나는경우 시너지 바로가기의 속성에서 관리자모드로 실행에 체크를 하신후 사용해보시기 바랍니다.

듀얼스크린 사용 예



라이센스 : 프리웨어
사용언어 : 영어
설치방법 : 설치마법사

제작사 홈페이지
http://synergy2.sourceforge.net/

다운로드



프로그램을 각각 설치하고 프로그램을 실행합니다.



설정을 하기전 사용하는 컴퓨터의 네트워크이름을 알아야 합니다.



Screens의 +를 눌러 컴퓨터를 등록합니다. 서버와 클라이언트의 네트워크이름





컴퓨터를 추가후 링크를 추가합니다.
스크린을 어디에 둘건지 확인하여 추가를 누르면 됩니다. 링크가 잘못되면 마우스가 서브스크린으로 넘어갔다가 안돌아 오는경우가 있으니 하나가 왼쪽이면 하나는 반드시 오른쪽에 위치해야 합니다.
위로 설정하면 다른 하나는 반드시 아래쪽에 위치시킨후 추가합니다.



아래 설정의 예
삼성컴퓨터는 컴팩컴퓨터의 위쪽에
컴팩컴퓨터는 삼성컴퓨터의 아래에 위치하라는 설정입니다.



자동시작 설정





설정을 한후 Start를 누르면 서비스가 시작됩니다.





다음은 클라이언트의 설정방법입니다.



서버컴퓨터의 이름을 등록후 Advanced를 누릅니다.



1. 클라이언트의 컴퓨터 이름입력.
2. 서버의 아이피 입력후 확인.



클라이언트에서도 자동시작설정을 할수가 있습니다.





설정이 완료되었습니다. Start를 눌러 서버와의 연결을 기다리시면 됩니다.
듀얼모니터 사용하듯 네트워크로 연결된 컴퓨터로 마우스가 자유자제로 넘어다는걸 보시는 일만 남았습니다.



관련글 보기
2008/12/20 - [시스템관리] - 원격 데스크탑 연결시 계정 제한 때문에 로그온할 수 없을경우 해결방법
2008/01/17 - [시스템관리] - 원격제어 리모트어드민 Remote administrator 빠른 바로가기 만들기
2007/10/21 - [윈도우즈/컴퓨터] - 원격 제어 관리프로그램 - Remote Administrator v2.1
Trackback Address :: http://joyholic.kr/trackback/331 관련글 쓰기
Tracked from free microsoft office 2010 | 2012/05/16 07:13 | DEL
Joy Holic!! - 마우스, 키보드 공유 프로그램 - 시너지 (Synergy v1.31)
Name
Password
Homepage
Secret
2009/02/23 11:21
Original :
http://social.microsoft.com/Forums/en-US/vsx/thread/9db42be5-46ae-494b-b688-c96943614f98



Hi

 

i need to write a wizard which generate a solution depending on the platform selected by the user. I.e. The user could need to generate an executable for CE - armv4 or for win32 or for another platform, the project could be in debug o release mode, etc..

i've found the dte configurationmanager contains all the informations i need (Supportedplatforms, activeconfiguration, platformname and so on), but i can't find a way to set the active configuration and platform.

msdn says to use envdte.solutionconfiguration.activate(), but how can i tell it which configuration i wish to activate?

To understand how to make this wizard, i've used an old atl wizard for ce which call the following functions:

x = new ActiveXObject("ProjWiz.SDProjWiz2.2");

...

EnsureDevicePlatforms();

...

CreateDeviceProject(strProjectName, strProjectPath);

but i can't find anything on the internet, to understand where this method come from or do or can be used.

 

thanks in advance

Stefano  






Reply::

Assuming that you know the configuration name ("Debug", "Release", etc.) and the platform name ("Any CPU", "Itaninium", etc.) you can use:

 

Dim objSolutionConfiguration2 As EnvDTE80.SolutionConfiguration2

 

For Each objSolutionConfiguration2 In DTE.Solution.SolutionBuild.SolutionConfigurations

 

   If objSolutionConfiguration2.Name = "Release" And objSolutionConfiguration2.PlatformName = "Itanium" Then

      objSolutionConfiguration2.Activate()

      Exit For

   End If

 

Next


 

Trackback Address :: http://joyholic.kr/trackback/299 관련글 쓰기
Name
Password
Homepage
Secret
2008/11/19 13:25
커스텀 AppWizard를 하나 만들어 봤습니다.

이 AppWizard로 프로젝트를 만들면 확장 DLL이 하나 만들어 지는데요,
File->New하구 Projects 탭을 보면 "커스텀 어플리케이션 위저드"라는 항목이 이것입니다.
단계는 하나 입니다. Project Name을 기입하고 OK를 누르면 Option1, Option2라고 하는
항목이 있습니다. 여기서 Option2에 아무 문자열이나 입력을 하면 프로젝트가 만들어 질때 프로젝트이름과 같은
txt파일 추가되고, 그렇지 않으면 생기지 않습니다.

그럼 소스 설명으로 넘어가서 Sample프로젝트의 Workspace의 FileView를 보면 Source Files,Header Files,
Resources Files,Help Files,Template Files 등이 있는데 Template Files를 제외한 것들은 커스텀 AppWizard
자체를 위한 소스들이구, Template Files에 있는 것들은 이 커스텀 AppWizard로 프로젝트를 만들때 생기는
파일들입니다.

Template Files에서 중요한 파일은 newproj.inf인데요, 다음과 같습니다.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$$// newproj.inf = template for list of template files
$$//  format is 'sourceResName' \t 'destFileName'
$$//    The source res name may be preceded by any combination of '=', '-',  '!', '?', ':', '#', and/or '*'
$$//       '=' => the resource is binary
$$//       '-' => the file should not be added to the project (all files are added to the project by default)
$$//       '!' => the file should be marked exclude from build
$$//       '?' => the file should be treated as a help file
$$//       ':' => the file should be treated as a resource
$$//       '#' => the file should be treated as a template (implies '!')
$$//       '*' => bypass the custom AppWizard's resources when loading
$$//    if name starts with / => create new subdir

/res
main.cpp    $$root$$.cpp
main.rc    $$root$$.rc
main.def    $$root$$.def
resource.h    resource.h
Stdafx.cpp    Stdafx.cpp
Stdafx.h    Stdafx.h
FormView.cpp    $$ROOT$$View.cpp
FormView.h    $$ROOT$$View.h
:res.rc2    res\$$root$$.rc2

$$IF(OPTION2)
ReadMe.txt    $$root$$.txt
$$ENDIF
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

주석은 $$// 로 시작합니다. 처음에 설명이 있는데 이거는 읽어 보시구요. 내용을 보면
처음에 /res로 시작해서 res 폴더를 생성합니다. 이걸 만들지 않으면 다음 ":res.rc2    res\$$root$$.rc2"
부분에서 에러가 납니다. 폴더를 먼저 만들고 해당 폴더에 파일을 카피해야합니다.
다음으로 "main.cpp    $$root$$.cpp" 는 Template Files에 있는 main.cpp파일을 프로젝트 이름.cpp
로 카피 하라는 명령입니다. 여기서 $$root$$ 는 프로젝트 이름을 의미하느 매크로 입니다.
가령 프로젝트이름을 Test로 한다면 다음 FormView.cpp    $$ROOT$$View.cpp  는
Template Files에 있는 FormView.cpp파일을 프로젝트 생성시 TESTView.cpp로 카피하라는 의미입니다.
$$ROOT$$는 프로젝트 이름을 전부 대문자로 표시하구요, $$Root$$나 $$root$$는 사용자가 기입한
대소문자 그대로를 사용합니다.

$$IF(OPTION2)는 OPTION2가 설정이 되어 있으면 이라는 의미입니다.
여기서 OPTION2는 m_Dictionary라는 CMapStringToString라는 클래스의 객체에 저장이 됩니다.
사용자가 1단계에서 Option2에 아무 문자열이나 입력을 하면
Sampleaw.m_Dictionary[_T("OPTION2")] = m_strOpt2; 와 같이 문자열이 저장이 됩니다.

다음으로 Template Files에 confirm.inf는 프로젝트 생성시 마지막에 확인하는 문구입니다.
여기서도 $$IF(OPTION2)를 써서 경우에 따라 달리 만들 수 있습니다. 예제에는 빠져 있으니 나름대로 작성해보세요^^;

다음으로 중요한 부분은 CSampleAppWiz클래스 인데요,
void CSampleAppWiz::InitCustomAppWiz() 는 커스텀 AppWizard가 처음 시작되면 호출 됩니다.
예제에 m_Dictionary[_T("PROJTYPE_DLL")] = _T("1"); 라는 것이 있는데, 이 커스텀 AppWizard로 프로젝트를
만들면 이것은 DLL이라는 것입니다. m_Dictionary에 PROJTYPE_DLL키에 아무값이나 주면 됩니다.
PROJTYPE_MDI 는 MDI Exe를 ,PROJTYPE_SDI는 SDI Exe를, PROJTYPE_DLG는 Dialogbase Exe를,
PROJTYPE_LIB는 static library를 ,PROJTYPE_CON는 console application을 만들라는 의미입니다.

void CSampleAppWiz::CustomizeProject(IBuildProject* pProject)함수는 커스텀 AppWizard로 만든 프로젝트의 Setting를
변경하거나 추가하는 등의 일을 합니다.
Sample에서는 _USRDLL옵션을 지우고,_AFXEXT 옵션을 추가하고(확장 DLL로 만듬), 링크 옵션에 생성되는 파일의
경로를 Custom AppWizard라는 폴더로 지정합니다. 이 옵션들은 Alt+F7을 눌러 프로젝트의 세팅을 설정하는 대화상자의
C/C++,Link 탭에 있는 내용을 프로그래밍으로 설정한 것입니다. IBuildProject는 VC COM입니다.

그럼 이 정도로 설명을 마치겠습니다.
그럼 즐푸... ^^;
Trackback Address :: http://joyholic.kr/trackback/281 관련글 쓰기
Name
Password
Homepage
Secret
2008/11/19 13:23
플그램을 만들다 보면 이런게(Custom AppWizard) 필요하다가 느낄 때가 간혹 있지요.

1.Custom AppWizard란
 뭐 특별한거 없습니다. 한 마디로 자신만의 AppWizard 이지요.

 VC 메뉴에서 File->New하면 나오는 대화상자에서 두 번째 Project탭을 보면
 여러분이나 저나 주로 쓰는 MFC Application(exe), MFC Application(dll),Win32 Application 등등
 프로젝트 유형이 17개 정도 있는데 그 중에 한 가지 입니다.

 이걸 쓰는 예를 한 가지들면 MDI프로그램 그램을 만들때 Client 화면이  각각 다 달라서
 화면 하나에 DLL하나를 쓰는 경우가 있습니다.
 이 때 dll 코딩 중의 상당 부분이 똑같이 반복 되는 경우 사용하면 편합니다.

 물론 MFC Application(dll) 프로젝트로 시작해서 Copy & Paste하면 되지만, Custom AppWizard하나 만들어서
 사용하면 프로젝트가 만들어 질때 원하는 code와 리소스 파일등등이 저절로 만들어 집니다.
 이렇게 되면 일일이 Copy & Paste할 필요가 없어서 편하다는 말이지요.

2.Custom AppWizard 생성 방법 3가지
 Custom AppWizard를 선택하고 프로젝트를 시작하면 3가지 방법 중 하나를 선택하라고 나옵니다.
 첫째로 기존의 Custom AppWizard 프로젝트(.dsw)를 불러와서 사용하는 방법인데요, 이거는 기존에 만들어
 놓은거에다가 원하는 것을 추가하거나 삭제하는 정도입니다.

 둘째로 VC에서 사용하는 기존의 것을 갖다가 쓰는 방법입니다. 이거는 여러분이 MDI나 SDI,Dialog Base등등
 기존에 사용하고 있는 것들에다가 원하는 것을 추가 하거나 삭제 하는 식으로 만드는 방법입니다.

 마지막으로 맨땅에 헤딩해서 만드는 건데, 처음부터 자기가 만드는 방법입니다.

 언뜻보기에는 두 번째가 좋을 거 같은데 그렇지도 않습니다.
 어찌나 복잡한지 분석하기도 헷갈립니다. 그냥 3번째 방법을 추천합니다. 물론 기존 VC에 있는거에다
 추가하거나 삭제할때는 2번째가 낮겠지요.

이렇게 해서 프로젝트를 만들고 빌드하면 *.awx라는 파일이 만들어지는데 이거 DLL입니다.
이 파일이 VC의 Template폴더에 카피가 됩니다.
그럼 이제 File->New하고 Project 탭을 보면 하나가 더 추가되어서 나오지요.

시간이 되면 예제와 함께 설명을 곁들여 다시 올리겠습니다.
그럼 즐푸~~~ ^^;
Trackback Address :: http://joyholic.kr/trackback/280 관련글 쓰기
Name
Password
Homepage
Secret
2008/11/19 13:16

출처 : Devpia, 김연기님
쓰레드(Thread)


프로세스가 실행이 되면 쓰레드가 실행 파일의 엔트리 포인트에서부터 프로그램을 실행 시킵니다.

쓰레드는 커널 오브젝트와 주소공간, 두 개의 구성요서를 가집니다.

Ø  커널 오브젝트 : 시스템이 쓰레드를 실행 하고 관리할 수 있도록 쓰레드의 정보를 가지고 있습니다.

Ø  주소공간(Address Space) : 프로그램 관점에서 볼 때 우리는 변수는 힘, 스택(지역변수), 전역공간(전역변수)에 저장이 된다고 배웠습니다.

주소공간은 스택을 말하는 것이고, 스택에는 지역 변수와 쓰레드의 실행코드가 들어 있습니다.

 

쓰레드의 실행과정


프로세스가 서브시스템에 로드 되고 쓰레드가 실행이 될 때는 커널에서 쓰레드 우선 순위에 따라 쓰레드를 실행 시킵니다.

커널에서 쓰레드를 우선 순위에 따라 쓰레드를 실행하는 작업을 쓰레드 스케쥴링(thread scheduling)이라고 하며, 커널에서 이 작업을 하는 모듈을 디스패쳐(dispatcher)라고 합니다.

디스패쳐(dispatcher)는 쓰레드의 우선 순위를 확인하고, 아래의 7가지 특성을 확인하고 실행을 합니다.

**영어를 한글로 번역하다 보니 공통적인 의미가 있습니다.

한글이름은 제가 임의로 번역한 것이지 공용적으로 사용되는 의미는 아닙니다.

영어로 알아 두시는 편이 좋을 것입니다.             

Ø  초기화(Initialized) : 쓰레드가 생성된 상태입니다.

Ø  준비(Ready) : 쓰레드가 실행되기를 기다리는 상태 입니다. 디스패쳐

dispatcher)는 우선순위를 확인하고 준비(ready)상태인 쓰

레드를 먼저 실행 합니다.

Ø  실행(Running) : 현재 쓰레드와 이전 쓰레드의 컨텍스트의 교환(Context

  Switch)() 완료 되어 쓰레드의 실행에 들어간 상태.

Ø  대기(Standby) : 대기(Standby)상태는 해당 쓰레드가 특정 CPU에 다음으

로 실행될 쓰레드를 대기(Standby) 상태라고 합니다.

Ø  기다림(Waiting) : I/O작업을 하면서 동기화 오브젝트에 의해 일시중지

(Waiting) 상태. 즉 해당 IO가 완료될 때까지 기다리고

있는 상태를 말합니다.

Ø  변환(Transition) : 쓰레드가 실행되기 전 상태. 준비(Ready)상태와의 차이

점은 커널 스택에 쓰레드 프로시져(Procedure) 코드 영

역이 올라온 상태는 준비(Ready)상태이고, 올라오지 않

은 상태는 변환(Transition)상태입니다.

Ø  종료(Terminated) : 쓰레드의 실행이 종료된 상태입니다.

 

아래 그림은 쓰레드의 생성->실행->종료까지 나타낸 그림입니다.

숫자는 실행되는 순서를 나타냅니다.

그림 출처 : Windows Internals 4th

 

쓰레드의 사용


Win32 API에서 쓰레드를 생성하는 함수는 CreateThread()함수와 _beginthrea

d 가 있습니다.

MFC에서는 AfxCreateThread를 사용할 수 있습니다.

 

CreateThread


HANDLE WINAPI CreateThread(

  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,

  __in       SIZE_T dwStackSize,

  __in       LPTHREAD_START_ROUTINE lpStartAddress,

  __in_opt   LPVOID lpParameter,

  __in       DWORD dwCreationFlags,

  __out_opt  LPDWORD lpThreadId

);

lpThreadAttributes : SECURITY_ATTRIBUTES구조체의 포인터를 입력하는 인자입니다.

 

dwStackSize : 쓰레드 스택 사이즈를 설정합니다.  0으로 두면 쓰레드 스택사이즈는 시스템 가상메모

범위내에서 정해집니다. 기본으로 설정된 사이즈는 1Mbyte입니다.

http://msdn2.microsoft.com/en-us/library/ms686774(VS.85).aspx

 

lpParameter : 쓰레드 프로시져 함수에 넘겨줄 데이터를 입력하는 인자입니다.

dwCreationFlags : 쓰레드를 생성된 상태를 설정합니다. CREATE_SUSPEND 설정하면

 ResumeThread() 호출하기 전까지 동작하지 않습니다. 0으로 주면 즉시 실행

하게 됩니다.

lpThreadId  : 쓰레드 ID 입력 받는 인자입니다.

http://msdn2.microsoft.com/en-us/library/ms682453.aspx

Return : 쓰레드 생성에 성공하면 쓰레드의 핸들을 리턴 합니다.

 

SECURITY_ATTRIBUTE 해당 프로세스/쓰레드/파일 등을 생성할 외부로부터 접근할 권한을 설정하는 역할을 합니다.

typedef struct _SECURITY_ATTRIBUTES {
  DWORD nLength;
  LPVOID lpSecurityDescriptor;
  BOOL bInheritHandle;

} SECURITY_ATTRIBUTES,
 *PSECURITY_ATTRIBUTES,
 *LPSECURITY_ATTRIBUTES;

nLength : SECURITY_ATTRIBUTES 구조체의 사이즈

lpSecurityDescriptor : 오브젝트의 권한을 설정하는 구조체입니다.

SECURITY_DESCRIPTOR구조체를초기화 하고 설정하는 방법은 다음 링크를 참조하세요.

http://msdn2.microsoft.com/en-us/library/aa446595(VS.85).aspx

bInheritHandle  : TRUE이면 상속 받을수 있습니다.

http://msdn2.microsoft.com/en-us/library/aa379560(VS.85).aspx

 

CreateThread로 생성된 쓰레드는 CloseHandle로 종료 시킵니다.

 

_beginThread/_beginthreadex


uintptr_t _beginthread(

   __in void( __cdecl *start_address )( void * ),    /*쓰레드 프로시져(Procedure 함수*/

   __in_opt unsigned stack_size,  /*쓰레드 스택 사이즈 기본 1Mbyte*/

   __in_opt void *arglist   /*쓰레드 프로시져에 인자로 넘길 데이터*/

);

uintptr_t _beginthreadex(

   __in_opt void *security,/*SECURITY_ATTRIBUTE 구제체 포인터*/

   __in_opt unsigned stack_size, /*스택 사이즈*/

   __in unsigned ( __stdcall *start_address )( void * ), /*쓰레드 프로시져*/

   __in_opt void *arglist, /*쓰레드 프로시져에 넘길 데이터*/

   __in_opt unsigned initflag, /*초기 상태 설정*/

   __out_opt unsigned *thrdaddr /*쓰레드 ID*/

);

Beginthread/beginthreadex 함수의 인자는 CreateThread와 비슷합니다.

Beginthread/beginthreadex 함수를 사용하여 만든 쓰레드는 CloseHandle을 함수로 종료 시킵니다.

그러나 쓰레드 프로시져는 계속 실행되고 있음을 유의해야 합니다.

이를 방지하기 위해서는 프로시져 함수내에서 플래그를 두어 쓰레드 프로시져가 리턴을 하던지 _endthread/_endthreradex 함수가 호출이 되어야합니다.

다음 예제는 _beginthreadex로 쓰레드를 생성하여 쓰레드 사용이 필요없을 때

CloseHandle을 호출하고 프로시져내에서 쓰레드를 안전하게 종료하는 예입니다.

 

unsigned _stdcall CallThreadHandlerProc(void *pThreadHandler)

{

        while (bExit == FALSE) /*쓰레드가 종료 될 때 bExit TRUE로 두어 빠져나오게합니다*/

        {

                //뭔가를 합시다.(Do Something -_-;;)

        }

/*_endthreadex함수를 호출하여 쓰레드가 정상적으로 종료 되게 합니다.*/

        DWORD exitCode;

        GetExitCodeThread(InputThrd, &exitCode);

        _endthreadex(exitCode);

        return 0;

}

 

_beginthread로 시작한 쓰레드는 반드시 _endthread로 종료하고,

_beginthreadex 로 시작한 쓰레드는 _endthreadex로 종료 시켜야합니다.

, 짝을 맞춰주어 종료 시켜야 안전하게 종료 됩니다.

 

CreateThread함수와 BeginThread/BeginThreadEx의 중요한 차이점이 있습니다.

Createthread로 생성된 쓰레드 내에서는 CRT함수를 쓰면 메모리 릭이 발생하고,

프로세스가 아무런 에러 리포팅이나 경고 없이 종료 되는 현상이 발생 할 수

있습니다.

자세한 내용은 Windows Via C/C++ 이나 Jeffrey책을 참조 하시길 바랍니다.

AfxBeginThread


MFC에서 쓰레드는 AfxCreateThread를 이용하여 쉽게 만들어 질수 있습니다.

AfxCreateThread  아래와 같이 선언되어있습니다.

 CWinThread* AfxBeginThread(

   AFX_THREADPROC pfnThreadProc,

   LPVOID pParam,

   int nPriority = THREAD_PRIORITY_NORMAL,

   UINT nStackSize = 0,

   DWORD dwCreateFlags = 0,

   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL

);

CWinThread* AfxBeginThread(

   CRuntimeClass* pThreadClass,

   int nPriority = THREAD_PRIORITY_NORMAL,

   UINT nStackSize = 0,

   DWORD dwCreateFlags = 0,

   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL

);

CreateThread _beginthread와 비슷한 인자를 가지 므로 따로 설명은 하지 않겠습니다.

두번째 선언에서 CRuntimeClass* 타입의 인자를 받습니다. CWinThread클래스를 상속 받아 UI를 가지는 쓰레드를 생성할 수 있습니다.

쓰레드를 종료할 때는 프로시져 함수에서 플래그 변수를 두어 종료 할수 있게 하고 AfxEndThread함수를 호출하거나 리턴을 하면 정상적으로 종료가 됩니다.

 

세가지 쓰레드 함수의 차이점

앞에서 본 것과 같이 Win32 API /MFC에서는 세 가지 함수를 이용해서 쓰레드를 생성할 수 있습니다.

이 세가지 쓰레드의 차이점은 CRT 함수를 안전하게 쓸 수 있는가 없는가에 차이가 있습니다.

결론을 말하자면 CreateThread CRT함수를 쓰면 메모리릭이나 프로그램의 비정상적인 종료 같은 문제를 발생 시킵니다.

_beginthread AfxBeginThread함수는 CRT 함수를 안전하게 쓸수 있습니다.

AfxBeginThread함수는 내부적으로 _beginthreadex함수를 호출합니다.

아래의 코드는 AfxBeginThread함수 코드입니다.

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,

        int nPriority, UINT nStackSize, DWORD dwCreateFlags,

        LPSECURITY_ATTRIBUTES lpSecurityAttrs)

{

#ifndef _MT

        pfnThreadProc;

        pParam;

        nPriority;

        nStackSize;

        dwCreateFlags;

        lpSecurityAttrs;

 

        return NULL;

#else

        ASSERT(pfnThreadProc != NULL);

 

        CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);

        ASSERT_VALID(pThread);

        //CWinThread::CreateThread함수를 호출하여 _beginthreadex를 호출합니다.              

        if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,

             lpSecurityAttrs))

        {

               pThread->Delete();

               return NULL;

        }

        VERIFY(pThread->SetThreadPriority(nPriority));

        if (!(dwCreateFlags & CREATE_SUSPENDED))

               VERIFY(pThread->ResumeThread() != (DWORD)-1);

 

        return pThread;

#endif //!_MT)

}

 

 

 

BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,

        LPSECURITY_ATTRIBUTES lpSecurityAttrs)

{

#ifndef _MT

        dwCreateFlags;

        nStackSize;

        lpSecurityAttrs;

 

        return FALSE;

#else

        ENSURE(m_hThread == NULL);  // already created?

 

        // setup startup structure for thread initialization

        _AFX_THREAD_STARTUP startup; memset(&startup, 0, sizeof(startup));

        startup.pThreadState = AfxGetThreadState();

        startup.pThread = this;

        startup.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);

        startup.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);

        startup.dwCreateFlags = dwCreateFlags;

        if (startup.hEvent == NULL || startup.hEvent2 == NULL)

        {

               TRACE(traceAppMsg, 0, "Warning: CreateEvent failed in CWinThread::CreateThread.\n");

               if (startup.hEvent != NULL)

                       ::CloseHandle(startup.hEvent);

               if (startup.hEvent2 != NULL)

                       ::CloseHandle(startup.hEvent2);

               return FALSE;

        }

 

        // create the thread (it may or may not start to run)

        //_beginthreadex함수를 호출합니다.

        m_hThread = (HANDLE)(ULONG_PTR)_beginthreadex(lpSecurityAttrs, nStackSize, 

             &_AfxThreadEntry, &startup, dwCreateFlags | CREATE_SUSPENDED, (UINT*)&m_nThreadID);

        if (m_hThread == NULL)

               return FALSE;

 

        // start the thread just for MFC initialization

        VERIFY(ResumeThread() != (DWORD)-1);

        VERIFY(::WaitForSingleObject(startup.hEvent, INFINITE) == WAIT_OBJECT_0);

        ::CloseHandle(startup.hEvent);

 

        // if created suspended, suspend it until resume thread wakes it up

        if (dwCreateFlags & CREATE_SUSPENDED)

               VERIFY(::SuspendThread(m_hThread) != (DWORD)-1);

 

        // if error during startup, shut things down

        if (startup.bError)

        {

               VERIFY(::WaitForSingleObject(m_hThread, INFINITE) == WAIT_OBJECT_0);

               ::CloseHandle(m_hThread);

               m_hThread = NULL;

               ::CloseHandle(startup.hEvent2);

               return FALSE;

        }

 

        // allow thread to continue, once resumed (it may already be resumed)

        ::SetEvent(startup.hEvent2);

        return TRUE;

#endif //!_MT

}

 

그렇다면 CreateThread함수와 _beginthread 함수의 차이점이 무엇인지 궁금할 것입니다.

_beginthread 함수도 코드를 보면 CreateThread  함수를 호출합니다.

이유는 CreateThread함수는 커널이 쓰레드가 생성되었다는 것을 알수 있는 유일한 방법이기 때문입니다.

차이점은 _beginthread는 커널에 CRT함수들을 위한 데이터 공간을 주어서 안전하게 CRT 함수를 사용할 수 있도록 합니다.

**자세한 사항은 Windows Programming Via C/C++ 이나 Jeffrey책을 참조 하세요.

 

진짜 메모리릭이 발생하는지 테스트를 해보았습니다.

다음은 테스트 코드입니다.

프로젝트는 콘솔모드로 만들었고 사용자 입력을 받는 쓰레드와 CRT함수를 사용하는 쓰레드 두개를 사용합니다.

테스트를 위해서 CreateThread 프로시져와 _beginthreadex 프로시져 각각 따로 만들고 번갈아 가면서 테스트를 수행했습니다.

디버깅 툴은 IMB사의 Purify Plus를 사용했습니다.

BounceChecker를 많이 사용하시는데, Purify Plus는 코드에서 어떤 함수를 호출해서 메모리릭이나 에러가 났는지 정확하게 표시해주는 장점이 있습니다.

 

#include <Windows.h>

#include <iostream>

#include <istream>

#include <process.h>

 

//CreateThread 프로시져 함수

DWORD WINAPI ThreadProc(__in  LPVOID lpParameter);

DWORD WINAPI InputProc(__in  LPVOID lpParameter);

         

//_beginthreadex 프로시져 함수

unsigned _stdcall CallThreadHandlerProc(void *pThreadHandler);

unsigned _stdcall InputProc_BT(void *pThreadHandler);

 

 

BOOL bExit = FALSE;  //쓰레드 종료를 위한 플래그 변수

HANDLE ThrdHandle=0;

HANDLE InputThrd = 0;

DWORD thrdID;

int _tmain(int argc, _TCHAR* argv[])

{

        unsigned int ID1, ID2;

        //CreateThread를 이용해서 쓰레드 생성

        ThrdHandle = CreateThread(0,0, ThreadProc, 0, 0, &thrdID);

        InputThrd = CreateThread(0,0, InputProc, 0, 0, &thrdID);

       

//_beginthreadex 함수를 사용할 때 아래의 주석을 빼고 위의 CreateThread부분을 주석

//      ThrdHandle  = reinterpret_cast<HANDLE>(_beginthreadex(0,1024,CallThreadHandlerProc,0,0,&ID1));

//      InputThrd = reinterpret_cast<HANDLE>(_beginthreadex(0,1024,InputProc_BT,0,0, &ID2));

       

        WaitForSingleObject(ThrdHandle, INFINITE);

 

        CloseHandle(ThrdHandle);

        CloseHandle(InputThrd);

        return 0;

}

 

DWORD WINAPI ThreadProc(__in  LPVOID lpParameter)

{

        while (bExit == FALSE)

        {

               int  decimal, sign;

               char *buffer;

               double source = 3.1415926535;

                //CRT함수 호출

               buffer = _fcvt( source, 7, &decimal, &sign );

        }

       

        return 0;

}

 

DWORD WINAPI InputProc(__in  LPVOID lpParameter)

{

        char ch;

        std::cin>>ch;

       

bExit = TRUE;

       

        return 0;

}

 

unsigned _stdcall CallThreadHandlerProc(void *pThreadHandler)

{

        while (bExit == FALSE)

        {

               int  decimal, sign;

               char *buffer;

               double source = 3.1415926535;

                //CRT함수 호출

               buffer = _fcvt( source, 7, &decimal, &sign );

        }

        DWORD exitCode;

        GetExitCodeThread(InputThrd, &exitCode);

        _endthreadex(exitCode);

        return 0;

}

 

unsigned _stdcall InputProc_BT(void *pThreadHandler)

{

        char ch;

        std::cin>>ch;

        bExit = TRUE;

       

        DWORD exitCode;

        GetExitCodeThread(ThrdHandle, &exitCode);

        _endthreadex(exitCode);

        return 0;

}

 

 

CreateThread로 쓰레드를 만들었을 때 결과.

 

_beginthread로 쓰레드를 만들었을 때 결과

 

 

 

결론


쓰레드를 만드는데 CreateThread _beginthread함수가 있습니다.

MFC에서 쓰레드를 쉽게 만들려면 AfxBeginThread를 사용할수 있고, AfxBeginThread에서는 특별히 UI를 가지는 쓰레드를 만들 수 있습니다.

쓰레드 프로시져 내에서 CRT함수를 쓸 때 는 _beginthread 로 쓰레드를 만들어 안전하게 CRT함수를 쓸수 있습니다.

** CRT함수 라이브러리 변수에는 errno, _doserrno, strok, _wcsok, strerror, _strerror, tmpnam, tmpfile, asctime, _wsctime, gmtime, _evct, _fcvt 가 있습니다. 이상은 Jeffrey책의 내용이었구요, 제생각에는 C표준 함수 몇 개 더 있는것 같습니다.


참고 서적 : Windows Interanals 4th Edition

Windows Via C/C++

 

PS


메모리릭은 간단한 테스트로 확인 할 수 있었지만 프로그램이 갑자기 죽는 경우는 테스트 할수 없었습니다.

예전에 그런 경우가 있었는데 그게 CreateThread때문인지 OCX때문인지 원인을 알 수는 없었습니다.

원래 쓰레드 프로시져를 클래스로 만들어 쉽게 사용할 수 있는 방법에대해 쓸려고 했는데 쓰다보니 내용이 많아 졌습니다.

다음엔 동기화 오브젝트와 쓰레드 프로시져 클래스 설계 하는것에 대해 강좌(?)를 만들겠습니다.

긴 글 읽어주셔서 감사합니다.

리액션좀 주세요~~~T..T

Trackback Address :: http://joyholic.kr/trackback/279 관련글 쓰기
Name
Password
Homepage
Secret
2008/11/19 13:11

#define AFXBEGINTHREAD(pfnThreadProc,pParam) AfxBeginThread(pfnThreadProc, pParam, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL)

CWinThread* g_pThread  = NULL;

volatile bool g_bExitThread  = true;    // 이벤트를 써도 됨
CCriticalSection g_csExitThread;

// Create....

 g_csExitThread.Lock();
 g_bExitThread = false;
 g_csExitThread.Unlock();

 g_pThread = AFXBEGINTHREAD(ThreadFunc,아무값);
 if(NULL == g_pThread)
 {
      // error...
 }

 g_pThread->m_bAutoDelete = FALSE;
 g_pThread->ResumeThread();





// Destroy...

 g_csExitThread.Lock();
 g_bExitThread = true;
 g_csExitThread.Unlock();

 if(NULL != g_pThread)
 {
          ::WaitForSingleObject(g_pThread->m_hThread,INFINITE);
          delete g_pThread;
          g_pThread = NULL;
 }



// Thread Function...

UINT ThreadFunc(LPVOID lpParam)
{
          while(true)
          {
                    Sleep(1);

                    g_csExitThread.Lock();
                    if(true == g_bExitThread)
                    {
                              g_csExitThread.Unlock();
                              break;
                    }
                    else
                    {
                              g_csExitThread.Unlock();

                              // 작업...
                    }

            }
            return 0L;
}

Trackback Address :: http://joyholic.kr/trackback/278 관련글 쓰기
Name
Password
Homepage
Secret
2008/11/19 13:04

출처 : Devpia

제가 볼려고 정리한건데요. (다른분들 글도 인용좀 했는데 출처가 어디였는지;;;;)
잘못 알고 있는거도 있을수 있습니다. 고수님들 잘못된거 답글 부탁드립니다. ㅎ

배포전에 Dependency를 이용 꼭 필요한 파일의 목록을 확인한다.(8버젼 부터는 안먹히기도 하나 그래도 꼭 확인한다)
VC에 있는 Dependency툴은 단일파일로 실행되고 한번만 실행해주면
윈도우 탐색기 컨텍스트 메뉴에 자동으로 View Dependencies를 추가해준다.

VC 6 이하(~ VS 6)
 MSVCRT, MFC42 등의 MFC버젼과 관련된 DLL과 추가로 사용한 DLL을 같이 배포한다.
 98이후부터는 운영체제에 포함되어 있다.
참고로 http://activex.microsoft.com/controls/vc/mfc42.cab로 관련 DLL을 다운받을 수 있으며
ActivX 배포시 inf파일에 추가해서 자동으로 설치되게 만들 수도 있다.

VC 7(VS 2003)
MSVCR71, MFC71
이 DLL은 최신 운영체제라고 해서 더는 기본 내장을 해 주지않기 때문에 응용 프로그램이 알아서 자기 디렉터리나 윈도우 시스템 디렉터리에다 구비해야 한다.

VC 8이후(VS 2005~)
side by side asembly라는 기술이 도입되어 windows 디렉토리에 밑에 WinSxS(Windows Side-by-Side) 폴더에 추가 작업을 해야한다.
Dependency를 사용해서 관련 DLL을 같이 배포해도 제대로 실행된다는 보장을 할 수 없다.
관련 DLL들은 C:\Program Files\Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT(이하 2005기준)에서 얻을수 있으며
MFC를 안쓰고 release버전만 배포한다면 Microsoft.VC80.CRT.manifest, msvcp80.dll, msvcr80.dll만 배포해도 된다.
(msvcm80.dll은 매니지드C++용 crt라 native를 쓰는 경우는 배포하지 않아도 된다.)

배포방법으로는
1. 재배포 패키지를 이용한다. 링크에서 다운받아 설치해도 되며 다른 배포방법도 설명하고 있다.

설치 디렉토리에서 얻기
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\vcredist_x86

Microsoft Visual C++ 2005 SP1 재배포 가능 패키지(x86)
http://www.microsoft.com/downloads/details.aspx?familyid=200B2FD9-AE1A-4A14-984D-389C36F85647&displaylang=ko

Microsoft Visual C++ 2008 재배포 가능 패키지(x86)
http://www.microsoft.com/downloads/details.aspx?FamilyID=9b2da534-3e03-4391-8a4d-074b9f2bc1bf&DisplayLang=ko

2. 정적 라이브러리를 사용한다(포함) -> CRT(/MT), MFC, ATL 등
실행파일이 커지긴 하나 제일 간단하다. MFC 라이브러리를 사용시는 MFC라이브러리를 정적으로 포함하면 CRT도 자동으로 /MT로 변경된다. 고로 어디서든 기존보다 더 간단하다. 귀찮아서 ActiveX를 이렇게 배포한적있음 ㅡ.ㅡ;

용량은 기본 MFC 다이얼로그 프로젝트가 아무것도 추가안하고 52k정도에서 308k 정도로 커진다.


3. 인스톨쉴드(Install Shield), 설치 프로젝트를 이용한다.

4. .Net Framework를 설치한다.
managed 로 컴파일했다면 .Net Framework는 필수이다.

-> 추가! MFC,ATL을 사용안한 프로젝트에 한해서만 먹힙니다. 프레임웍만 깔면 WinSxS에 VC80.CRT관련만 설치되네요.

    VC90도 똑같습니다. 재배포 패키지를 설치하셔야 MFC,ATL관련이 깔립니다.

위 방법 외에도 몇가지가 더 있으며 재배포 패키지 링크에 연결된 설명들과 아래 링크들을 참조하기 바란다.
http://www.codeproject.com/cpp/vcredists_x86.asp(codeproject에 소개된 방법)
http://www.serious-code.net/moin.cgi/RedistributingVisualCppRunTimeLibrary

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

재배포 패키지를 이용한 OCX(ActiveX) 배포 (2005 ~)
Cab 파일들어갈 파일 내용은 다음과 같다.
자신이 만든 ocx파일
inf 파일
재배포패키지

INF파일의 내용은 다음과 같다.

[Setup Hooks]
hook1=hook1

[hook1]
run=%EXTRACT_DIR%\vcredist_x86.exe

[version]
signature="$CHICAGO$
AdvancedINF=2.0

[Add.Code]
mytest.ocx=mytest.ocx
vcredist_x86.exe=vcredist_x86.exe

[vcredist_x86.exe]
file-win32-x86=thiscab
DestDir=11
RegisterServer=no

[mytest.ocx]
file-win32-x86=thiscab
DestDir=11
FileVersion=1,0,0,3
clsid={1DF754CB-D85F-490B-9011-BDCB0BFDC430} <= 이건 아시져?
RegisterServer=yes

inf파일의 [Add.Code]는 나열된 순서의 반대로 설치가 되는 점을 유의하며 위와같이 하면
vc2005 재배포 패키지가 실행된후 맨 마지막으로 ocx가 설치된다.

Trackback Address :: http://joyholic.kr/trackback/277 관련글 쓰기
Name
Password
Homepage
Secret
2008/11/17 11:21

출처 : http://cafe.daum.net/itbankcafe/9FAr/24?docid=oTPO|9FAr|24|20040614014004&q=%C7%D1%B1%DB%BF%C0%C5%E4%B8%B6%C5%B8&srchid=CCBoTPO|9FAr|24|20040614014004

 

주제 : 휴대폰 한글 입력 UI와 오토마타 기초안

- 자모 일체형과 분리형 한글 입력 방안  예


글.  알치  오 상 문  ( sualchi@yahoo.co.kr )


--------------------------------------------------------------------------

I. 자모 키 겹침 방식


수알치 한글입력은 휴대폰 한글 입력에 있어 직관적인 쉬운 입력과 속기를 위한 방식
모두를 제공하는 키 입력 방식이다.


이 때 자판 배열을 직관적으로 쉽게 알아볼 수 있도록 배치했다.
이제 기본 한글 입력키 구성과 배열 그림을 살펴보겠다.


     

[그림 1] 키 배치 (제1안과 2안)


그림 1에서 볼 수 있듯이
자음은 좌에서 우, 위에서 아래로 ㄱ, ㄴ, ㄷ, ... 순서와 거의 유사하게 배치된다.
모음은 좌에서 우, 위에서 아래로 ㅏ, ㅑ, ㅓ, ... 순서로 배치된다.


네모[ㅋ]  : 1회 쉬프트 키. 이 키를 누르고(또는 누른 상태) ㄱ을 누르면 ㅋ이 입력된다.
         예; [ㅋ] + ㄷ = ㅌ


[한/영/부호]: 입력 모드를 바꾼다. 한글 -> 영문 -> 특수부호 -> 한글 순서로 모드 변경.
           이 키 입력은 현재 입력중인 한글 한 글자의 입력이 끝남을 의미하기도 한다.

           [참고] 길게 누르면 무조건 한글 모드로 바뀐다(한글모드가 기본 모드)


네모[ㅏ] : 다음에 누를 키가 모음임을 알린다(누른 상태로 모음 키 연속 입력 가능).
          만약, 자음 입력 없이 모음만 입력되면 자동으로 ㅇ이 입력된다(ㅏ = 아).
         
          예; [ㅏ]  + ㅑ = ㅑ
          예; 네모[ㅏ]키를 누른 상태에서 ㅑ + ㅣ = ㅒ
             [참고] 이것은 다음의 입력과 같다.  [ㅏ]  + ㅑ + [ㅏ]  + ㅣ
          예; 네모[ㅏ]키를 누른 상태에서 ㅜ + ㅓ + ㅣ = ㅞ
             [참고] 이것은 다음의 입력과 같다.  [ㅏ] + ㅜ + [ㅏ]  + ㅓ + [ㅏ]  + ㅣ  


수알치 한글 입력 오토마타는 양손과 한 손 입력 방식을 모두 지원한다.

만약 한글 영문 특수부호를 선택하는 키가 별도로 마련되어 있다면 ". ? ! ,"를 누를 수 있는 키로 할당하는 것도 가능하다.



한글 입력 오토마타 구현을 위한 규칙  


한글을 입력하는 오토마타는 한글을 쓰는 방식과 유사하다. 자음은 그대로 누르고 모음은 모음 입력을 알리는 키를 누른 후 입력한다. 또한 양손 입력과 한 손 입력을 모두 지원한다.
즉, 어떤 글자를 만들 때는 양손을 이용하거나 한 손을 이용할 수 있다는 것이다(보다 정확한 표현은 단타로 끊어서 치느냐 아니면 동시에 이중 키 눌림을 사용하느냐이다).


- 자음 키를 누르면 기본 자음이 입력된다.

  -> ㄲ, ㄸ, ㅃ, ㅆ ,ㅉ 복자음이 가능한 경우, 같은 글자를 반복하면 복자음이 된다.
      ㄱ = ㄱ
      ㄱ + ㄱ = ㄲ
      복자음이 안 되는 경우는 네모[ㅋ]키를 누른 상태와 같은 글자가 입력된다. (o + o = ㅎ)
  -> 만약 세 번을 입력하면 글자 옆의 글자가 입력된다.  
      ㄱ + ㄱ + ㄱ = ㅋ
     [참고] ㅋ을 입력하는 또 다른 방법은  네모[ㅋ] 키를 누른 후(또는 누른 상태)

                ㄱ을 누른다.


- 모음을 입력하려면  키를 누른 후(또는 누른 상태) 해당 모음 키를 누른다.

  -> 네모[ㅏ]키를 누른 상태에서 연속으로 모음을 입력할 수 있다.
      예;  를 누른 상태 + ㅜ + ㅓ + ㅣ = ㅞ

  -> 네모[ㅏ]키를 누른 상태에서 같은 모음을 연속 입력하면 ㅣ가 붙는다.
      예;  를 누른 상태 + ㅏ + ㅏ = ㅐ
      [참고] 이것은 다음 입력과 같다.   + ㅏ +   + ㅣ

      [참고] "[ㅏ]  + ㅏ + [ㅏ] + ㅏ" 입력인 경우 앞과 뒤의 "[ㅏ] + ㅏ" 입력을 연속으로
            볼 것인지 아니면 별개 입력으로 볼 것인지에 대한 처리를 결정해야 한다.
            (기본은 별개의 키 입력으로 처리한다.)
            - 별개의 키 입력으로 처리한다면 다음 예를 따를 것이다.
                예; [ㅏ] + ㅏ + [ㅏ] + ㅏ = 아아
            - 연속 키 입력으로 처리한다면 다음 예를 따를 것이다.
                예;  [ㅏ] + ㅏ + [ㅏ]  + ㅏ = 애

             (단, 앞에 자음이 있다면 모음 ㅇ 자리에 해당 자음이 들어간다.)



"대한민국" 입력 예


느리지만 쉬운 입력 예:
대 = ㄷ + [ㅏ] + ㅏ + [ㅏ]  + ㅣ     (5타)
한 = [ㅋ]  + o + [ㅏ]  + ㅏ + ㄴ     (5타)
민 = ㅁ + [ㅏ]  + ㅣ + ㄴ  (4타)
국 = ㄱ + [ㅏ]  + ㅜ + ㄱ  (4타)

빠른 예:
대 = ㄷ + [ㅏ](ㅏ+ㅏ)   
한 =  [ㅋ](ㅎ) + [ㅏ](ㅏ) + ㄴ       
민 = ㅁ +  [ㅏ](ㅣ) + ㄴ  
국 = ㄱ +  [ㅏ](ㅜ) + ㄱ          

[참고] 괄호 안의 내용은 괄호 바로 앞 [ ] 키를 누른 채 입력함

※ 초보자는 정상적인 입력 방식을 사용하면 쉽게 입력 방식을 익힐 수 있다.
또한 노련한 사용자는 동시타법, 연타, 자동완성(o 첨가와 ㅣ 추가 기능)을 사용하여
입력 속도를 높일 수 있다.



"꿩사냥꾼" 입력 예


느리지만 쉬운 입력 예:
꿩 = ㄱ + ㄱ + [ㅏ]  + ㅜ + [ㅏ]  + ㅓ + ㅇ  (7타)
사 = ㅅ + [ㅏ] + ㅏ    (3타)
냥 = ㄴ + [ㅏ]  + ㅑ + o     (4타)
꾼 = ㄱ + ㄱ + [ㅏ] + ㅜ + ㄴ   (5타)


빠른 예:
꿩 = ㄱ + ㄱ +  [ㅏ](ㅜ + ㅓ) + ㅇ  
사 = ㅅ + [ㅏ](ㅏ)    
냥 = ㄴ + [ㅏ](ㅑ) + o     
꾼 = ㄱ + ㄱ + [ㅏ]( ㅜ) + ㄴ  



다른 입력 예


깍뚜기  =  ㄱ + ㄱ + [ㅏ] (ㅏ)  + ㄷ + ㄷ + [ㅏ](ㅜ)  + ㄱ + [ㅏ](ㅣ)

월드컵  =  o + [ㅏ](ㅜ+ㅓ) + ㄹ + ㄷ +  [ㅏ](ㅡ)  + [ㅋ](ㄱ) + [ㅏ](ㅓ) + ㅂ

셀 =  ㅅ + [ㅏ](ㅜ+ㅓ+ㅓ) + ㄹ     또는  ㅅ + [ㅏ](ㅜ+ㅓ+ㅣ)

셸 =   ㅅ +  [ㅏ](ㅕ+ㅕ) + ㄹ    또는  ㅅ +  [ㅏ](ㅕ+ㅣ) + ㄹ  




--------------------------------------------------------------------------


II. 자모 키 분리 방식 (구현 예 1)



ㄱ = ㄱ
ㄱ ㄱ = ㄲ
ㄱ ㄱ ㄱ = ㅋ

ㄴ = ㄴ
ㄴ ㄴ = ㄹ

ㄷ = ㄷ
ㄷ ㄷ = ㄸ
ㄷ ㄷ ㄷ = ㅌ

ㅈ = ㅈ
ㅈ ㅈ = ㅉ
ㅈ ㅈ ㅈ = ㅊ  

ㅂ = ㅂ
ㅂ ㅂ = ㅃ
ㅂ ㅂ ㅂ = ㅁ

ㅅ = ㅅ
ㅅ ㅅ = ㅆ
ㅅ ㅅ ㅅ = ㅍ

ㅇ = ㅇ
ㅇ ㅇ = 빈칸

ㅎ = ㅎ
ㅎ ㅎ = ,
ㅎ ㅎ ㅎ = ~

. = .
. . = ?
. . . = !

ㅏ = ㅏ
ㅏ ㅏ = ㅓ
ㅏ ㅏ ㅏ = ㅑ
ㅏ ㅏ ㅏ ㅏ = ㅕ

ㅗ = ㅗ
ㅗ ㅗ = ㅜ
ㅗ ㅗ ㅗ = ㅛ
ㅗ ㅗ ㅗ ㅗ = ㅠ

ㅣ = ㅣ
ㅣ ㅣ = ㅡ

(예)
ㅔ = ㅏ ㅏ ㅣ
ㅝ = ㅗ ㅗ ㅓ
ㅞ = ㅗ ㅗ ㅓ ㅣ


(예) 딱따구리 = ㄷ ㄷ ㅏ ㄱ ㄷ ㄷ ㅏ ㄱ ㅏ ㅏ ㄹ ㅣ   (12타)
                  ㄸ ㅏ ㄱ     ㄸ ㅏ ㄱ    ㅜ ㄹ ㅣ
       [천지인] ㄷ ㄷ ㄷ ㅣ . ㄱ ㄷ ㄷ ㄷ ㅣ . ㄱ ㅡ . ㄴ ㄴ ㅣ   (17타)

(예) 대한민국 = ㄷ ㅏ ㅣ ㅎ ㅏ ㄴ ㅂ ㅂ ㅂ ㅣ ㄴ ㄱ ㅗ ㅗ ㄱ   (15타)
                ㄷ   ㅐ  ㅎ ㅏ ㄴ       ㅁ ㅣ ㄴ ㄱ    ㅜ ㄱ    
       [천지인]  ㄷ ㅣ . ㅣ ㅎ ㅣ . ㄴ ㅁ ㅣ ㄴ ㄱ ㅡ . ㄱ   (15타)

(예) 안녕하세요 = ㅇ ㅏ ㄴ ㄴ ㅏ ㅏ ㅏ ㅏ ㅇ ㅎ ㅏ ㅅ ㅏ ㅏ ㅣ ㅇ ㅗ ㅗ ㅗ  (19타)
                  ㅇ ㅏ ㄴ ㄴ         ㅕ  ㅇ ㅎ ㅏ ㅅ      ㅔ  ㅇ      ㅛ  
       [천지인]   ㅇ ㅣ . ㄴ . . ㅣ ㅎ ㅣ . ㅅ . ㅣ ㅣ ㅇ . . ㅡ  (18타)



--------------------------------------------------------------------------


II. 자모 키 분리 방식 (구현 예 2)



ㄱ = ㄱ   ㄱ ㄱ = ㅋ   ㄱ ㄱ ㄱ = ㄲ

ㅁ = ㅁ   ㅁ ㅁ = ㅂ  ㅁ ㅁ ㅁ = ㅃ

ㄴ = ㄴ   ㄴ ㄴ = ㄹ  ㄴ ㄴ ㄴ = ㅊ

ㅅ = ㅅ   ㅅ ㅅ = ㅇ  ㅅ ㅅ ㅅ = ㅆ

ㄷ = ㄷ   ㄷ ㄷ = ㅌ   ㄷ ㄷ = ㄸ

ㅎ = ㅎ   ㅎ ㅎ = ㅈ  ㅎ ㅎ ㅎ = ㅉ

ㅏ = ㅏ   ㅏ ㅏ = ㅑ

ㅓ = ㅓ   ㅓ ㅓ = ㅕ

ㅗ = ㅗ   ㅗ ㅗ = ㅛ

ㅜ = ㅜ   ㅜ ㅜ = ㅠ

ㅣ = ㅣ   ㅣ ㅣ = 빈칸

ㅡ = ㅡ   ㅡ ㅡ = 부호선택 ( . ? ! , ~ 등 )


(예)
ㅔ = ㅓ ㅣ
ㅝ = ㅜ ㅓ
ㅞ = ㅜ ㅓ ㅣ


(예) 딱따구리 = ㄷ ㄷ ㄷ ㅏ ㄱ ㄷ ㄷ ㄷ ㅏ ㄱ ㅜ ㄹ ㅣ  (13타)
                      ㄸ ㅏ ㄱ       ㄸ ㅏ ㄱ ㅜ ㄹ ㅣ

       [천지인] ㄷ ㄷ ㄷ ㅣ . ㄱ ㄷ ㄷ ㄷ ㅣ . ㄱ ㅡ . ㄴ ㄴ ㅣ   (17타)
 
(예) 대한민국 = ㄷ ㅏ ㅣ ㅎ ㅏ ㄴ ㅁ ㅣ ㄴ ㄱ ㅜ ㄱ   (12타)
                ㄷ   ㅐ  ㅎ ㅏ ㄴ ㅁ ㅣ ㄴ ㄱ ㅜ ㄱ  

       [천지인]  ㄷ ㅣ . ㅣ ㅎ ㅣ . ㄴ ㅁ ㅣ ㄴ ㄱ ㅡ . ㄱ  (15타)

(예) 안녕하세요 = ㅅ ㅇ ㅏ ㄴ ㄴ ㅓ ㅓ ㅅ ㅇ ㅎ ㅏ ㅅ ㅓ ㅣ ㅇ ㅗ ㅗ   (17타)
                     ㅇ ㅏ ㄴ ㄴ    ㅕ    ㅇ ㅎ ㅏ ㅅ   ㅔ  ㅇ    ㅛ  

       [천지인]  ㅇ ㅣ . ㄴ . . ㅣ ㅎ ㅣ . ㅅ . ㅣ ㅣ ㅇ . . ㅡ  (18타)

(예) 사랑해요 = ㅅ ㅏ ㄴ ㄴ ㅏ ㅅ ㅅ ㅎ ㅏ ㅣ ㅇ ㅗ ㅗ   (13타)
                ㅅ ㅏ    ㄹ ㅏ    ㅇ ㅎ    ㅐ ㅇ    ㅛ

       [천지인] ㅅ ㅣ . ㄴ ㄴ ㅣ . ㅎ ㅣ . ㅣ ㅇ . . ㅡ   (15타)


--------------------------------------------------------------------------


이상으로 한글 입력을 UI와 오토마타를 위한 기본 키 입력 예를 마칩니다.


알치 오상문.


<끝>

Trackback Address :: http://joyholic.kr/trackback/276 관련글 쓰기
Name
Password
Homepage
Secret
2008/11/17 11:18
오토마타(Automata) 이야기  UserPreferences
  
        Welcome   동우의 HomePage   주제분류   추천사이트   게시판   RecentChanges   WikiSandBox 

--------------------------------------------------------------------------------

Written by 한동훈


[강좌] 오토마타 이야기 (1)                   0               05/14 14:05    39

********************************************************************* 
                오토마타 이야기  1
********************************************************************* 

이야기틀 : 오토마타 이야기
이야기꾼 : 한동훈 ddoch@hitel.kol.co.kr
                  ddoch@nownuri.nowcom.co.kr
저작권   : GPL 
이야기날짜 : 1997.1.24

( 질문사항이나 기타사항이 있으시더라도 개인메일로 보내주지 마세요. ^^;
  제가 개을러서 답변을 못해드려서 죄송합니다. 자세한 사항은 관련
  개시판에 올려주시면 충실히 답변해 드리겠습니다. )

순 서 :

1. 들어가는 말
2. 오토마타란?
3. 간단한 예제 하나 
4. 오토마타 문자열 검색법
5. 한글과 오토마타  
6. 나오는 말


1. 들어가는 말

요즘들어서 본의 아니게 시간이 많아서 그동안 보지 못했던 알고리즘을 공부
하게 되었습니다. 그러든 중 항상 생각만 하고 있던 한글입력기를 만드는 작업에
착수하게 되었지요. 역시 오토마타라는 알고리즘을 이해를 하고 들어가니 한글
오토마타라는 것이 우리가 알고 있는 한글조립법의 상식을 표로 그려둔 것에 
지나지 않았습니다. 그래서 몇일동안뚝딱거려서 예전에 만들어둔 한글출력기
와 합쳐서 한글라이브러리를 완성했습니다. 한글입력기가 제대로 돌아가는 순
간 정말 기분은 하늘로 날라가는 것만 같았습니다. 이제는 제가 그동안 만들어
둔 재사용가능한 루틴들을 포함시키면서 패키징 작업에 들어가고 있습니다. 
(괜히 저 자랑같이 되어 버렸네요.. ^^;)

저번에 리눅스동호회 번개모임에 갔을 때, 어느분이 한글오토마타에 대해서 질
문을 하셨는데 제대로 답변을 못해서 미안할 따름이었는 데, 이제 이 강좌로
갈음 할 수 있었으면 좋겠습니다. 

이 강좌는 한글입력기를 구현하시고자 하는 분이나, 오토마타라는 알고리즘을
이해하시고자 하는 분들을 위해서 쓰여졌습니다. 그리고 실제 예제는 저의 한
글라이브러리 안에 있는 루틴들과, "C로 배우는 알고리즘", "컴퓨터 속의 한글"
를 참조하였음을 밝혀두겠습니다.

2. 오토마타란?

저도 예전에 그랬지만 한글오토마타와 한글입력기를 구분하지 못하시는 분들이
많이 있습니다. 한글오토마타는 오토마타라는 알고리즘을 이용해서 한글입력의
기본원리를 구현한 것이고, 한글입력기는 말그대로 한글입력을 처리하는 루틴
입니다. 한글입력기는 얼마던지 오토마타라는 알고리름을 사용하지 않고도 구
현이 가능하지만 오토마타라는 알고리름을 이용하는 것이 현재로선 가장 효율
적입니다. 

3. 간단한 예제 하나 

먼저, 오토마타라를 이해하기 위해서 오토마타는 아니지만 그 비슷한 예를 하나
들겠습니다.
일반적으로 한글을 처리하는 데서 많이 쓰이는, 어떤 문자열 내에서 몇번째 문
자가 영문인가? 아니면 한글 첫째 바이트인가? 둘째 바이트인가 를 알아내는
함수를 예로 들겠습니다. 

한글은 2바이트로 처리되며 첫바이트는 0x80(16진수) 이상이라는 것을 잘 알고
계실 겁니다. 한글완성형은 첫바이트던 둘째 바이트던 모두 0x80이상으로 맞춰
져 있습니다만 우리가 가꿔야 할 조합형은 아쉽게도 둘째 바이트가 0x80이하인
것이 더러 있습니다. 조합형으로 "가"는 16진수로 "88 61" 로 되는 데 0x61은
소문자 'a'입니다. 즉, 어느 문자열 중에서 a가 나온다고 해서 무조건 영문자
라고만 볼 수 없다는 이야기입니다. 조합형 한글 둘째 바이트가 될 수 있다는
이야기죠.
한글이 포함된 문자열이 있다고 했을 때, 어느 위치의 바이트가 종류가 어떻게
되는 지 오토마타적 알고리즘으로 생각해봅시다. 단 문자열의 처음에는 한글둘
째 바이트로 시작해서는 안된다는 전제가 있어야 합니다.
영문자면 0, 한글 첫째바이트이면 1, 한글 둘째 바이트이면 2로 가정하겠습니다.

문자열 :  char str[8] = "a각b한z" 

위의 문자열에서 배열의 첨자형식으로 인덱스를 따지면, 0번째 'a'는 당연히
0이고, 1번째는 '1', 2번째는 '2', 3번째는 '0', 4번째는 '1', 5번째는 '2'...
이런 식으로 될것입니다. 즉, 상태 0은 영문자, 상태 1은 한글 첫째 바이트, 
상태2는 한글둘째바이트로 하겠습니다.

                               
      
   128 이상                     무조건  
+->  상태0 ------------------>  상태1 -----------------> 상태2
|    ^   |                        ^                        | |
|    |   | 128 미만               +------------------------+ |
|    +---+                                                   |
|                                                            |
+------------------------------------------------------------+ 


간단히 그려본 상태변화도 입니다. 문자열을 검사하기전에 상태0로
초기화를 하고나서 문자열을 처음부터 하나씩 검사를 해서 그 문자값이 128
(0x80)이상일 경우와 128미만 일 경우로 나누어 집니다. 
즉, 처음의 상태0에서 128미만의 글자가 처음으로 나오면 당연히 상태0
이 되고, 128이상의 글자가 나오면 한글첫째바이트 이므로 상태1로, 다음
바이트는 안보고도 한글두째바이트로 간주해서 상태2로 넘어갑니다. 상태2
에서 128미만의 글자가 나오면 영문자(128미만의 아스키문자)로 상태0이 
되고 128이상의 글자가 나오면 상태1로 됩니다. 이상태로 원하는 인덱스의
문자열이 나올때까지 계속 검사를 하면 문자의 종류를 알수 있습니다. 
사실 이정도는 그냥 간단하게 해도 되지만 일부러 오토마타적 사고방식으로
풀이를 했습니다. 그림을 못그렸지만 보시면 이해가 쉬울 겁니다. 
그럼, 위의 그림을 보고 한글종류를 돌려주는 함수를 작성해 보겠습니다.

함수원형 : int ishangul(unsigned char *str, int index);
인    자 : str은 검사대상 문자열, index는 배열첨자식 str내의 위치
반환  값 : 0 : 영문자 , 1 : 한글첫째바이트, 2 : 한글둘째바이트


#include <stdio.h>
#include <string.h>

#define IS_LOW_ASC   0      /* 128 미만의 아스키코드 */
#define IS_HIGH_ASC  1      /* 128 이상의 아스키코드 */
#define IS_ENGLISH   0      /* 상태 0 */
#define IS_HANGUL_FIRST 1   /* 상태 1 */
#define IS_HANGUL_SECOND 2  /* 상태 2 */

typedef unsigned char byte;
int  ishangul(byte *str, int index) {
  int state;
  int inMode;
  int key;
  int i = 0;
  
  if (strlen(str) < index + 1) return -1; /* 문자열길이가 index보다 작다면 */
  state = IS_ENGLISH;        /* 상태0으로 초기화 */

  while ((key = *(str + i)) != '\0') { /*문자열의 끝까지 */
    if (key < 128) inMode = IS_LOW_ASC; 
    else inMode = IS_HIGH_ASC;

    switch (state) {
      case IS_ENGLISH :       /* 상태0이고 128이상의 문자라면 한글 첫째 */
        if (inMode) state = IS_HANGUL_FIRST;
          else state = IS_ENGLISH; /* 그외에는 영문자 */
        break;
      case IS_HANGUL_FIRST : /* 한글첫째상태라면 한글둘째 */
        state = IS_HANGUL_SECOND;
        break;
      case IS_HANGUL_SECOND  : /* 한글 둘째 상태라면 */
        if (inMode) state= IS_HANGUL_FIRST; /* 128이상이라면 한글첫째 */
          else state = IS_ENGLISH;    /* 128미만이라면 영문자 상태 */
        break;
    }
    if (i++ == index) break; /* 찾는 인덱스까지 검사했다면 */ 
  }
  return state;  /* 한글의 종류를 리턴 */
}

아주 이해하기가 쉬울 겁니다. 때로는 조금 복잡한 것도 오토마타적 알고리즘
으로 생각하면 쉬울 때가 종종 있을 겁니다. 정확히 작동하는 지 한번 테스트
해보시기 바랍니다. 

다음 시간에는 오토마타 알고리즘을 사용한 문자열 검색법을 알아보겠습니다.

ddoch 한동훈 


#1809   시삽    (김성대  )
[강좌] 오토마타 이야기 (2)                   0               05/14 14:05    39

3. 오토마타 문자열 검색법

오토마타의 정확한 의미는 "이산적인 입력과 출력을 가지는 시스템의 수학적
모델" 입니다. 오토마타는 5개의 항목으로 구성됩니다.
(에디터로는 표현하기 곤란한 수학적인 기호가 들어가는 관계로, 기호대신
  적절한 낱말로 표현하겠습니다. )

상태집합, 초기상태, 종료상태, 입력문자집합, 상태전이 함수가 그것입니다.

좀 엉뚱하지만 재미있는 예를 하나 들겠습니다.

아릿다운 공주가 5층짜리 건물의 5층에 갇혀 있습니다. 각 1층부터 4층까지
층마다 흉칙한 괴물이 지키고 서있고, 통과하려면 죽음의 "가위바위보"를 해
서 이겨야 통과할 수 있습니다. 만일 이기면 한층위로 올라갈 수 있고, 지면
한층을 내려와야 합니다. 그런데 주인공이 낼 수 있는 경우는 오로지 "바위"
뿐입니다. 무승부가 되면 다시 가위바위보를 해야 합니다.
자, 이제 입구에서 부터 시작해서 5층까지 괴물이 가위바위보를 내는 경우에
따라 주인공은 몇층에 있는 지 생각해 봅시다.

[표 1]
-------------------------------------------------------
주인공이 있는 층 \ 괴물이 내는 가위바위보의 경우
                    가위바위         보
-------------------------------------------------------
입구               1층         입구         입구
1층                2층         1층          입구
2층                3층         2층          1층
3층                4층         3층          2층
4층                5층         4층          3층
5층                .            .            .
--------------------------------------------------------

이해가 쉽게 될 겁니다. 왼쪽은 주인공이 있는 층이고 오른쪽은 괴물이 내는
경우에 따라 달라지는 주인공이 있게 되는 층입니다. 이렇게 표를 만들어 두
면 주인공의 상황과 각 경우에 따라 어떻게 주인공의 상태가 변하는 가하는
것을 쉽게 알 수 있습니다. (예가 유치하죠? ^^;)

여기서 상태집합은 주인공이 있게 되는 층인 {입구, 1층, 2층, 3층, 4층, 5층}
입니다. 초기상태는 당연히 "입구"가 됩니다. 종료상태는 "5층"이 되겠죠?
입력문자집합은 괴물이 내는 {가위, 바위, 보}가 됩니다. 상태전이함수는
위의 표입니다. 현재의 상태에서 각 경우를 만나면 어떤상태를 얻을 수 있
다는 것이 상태전이 함수입니다. 너무 쉽죠?
즉, 간단하게 말하면 "어떤 상태가 주가 되고 외부적인 경우에 따라 그 상태가
어떻게 변하는가를 표로 나타낸 것"이 "오토마타 표"가 됩니다.

이제 이러한 오토마타 알고리즘으로 간단한 문자열을 검색해봅시다. 예를 간단
히 하기 위해서 "01"로만 예를 들었습니다.

원본 문자열 : "001011010101" : DEST
찾는 문자열 : "110101"       : SEARCH

위의 경우처럼 상태전이표를 그려보면 됩니다.

상태집합 = { 0, 1, 2, 3, 4, 5, 6}
초기상태 = 0
종료상태 = 6
입력문자집합 = {'0', '1'}
상태전이함수 =

[표 2]
------------------------------------
   상태\입력   '0'     '1'
------------------------------------
       0        0       1
       1        0       2
       2        3       2
       3        0       4
       4        5       2
       5        0       5
       6        .       .
------------------------------------

좀 더 알아보기 쉽게 상태전이도를 나타내면 다음과 같습니다.

                    +--------------------+   
                    |         1          |
      1        1 V   0           1    |     0          1
q0 --->  q1 ----> q2 ---->  q3 -------> q4 ------> q5 ------>  q6
                   ^ |  
                   | | 1
                   +-+ 

(각 상태에서 q0으로 가는 것은 부분적으로 생략..)
V는 화살표입니다. ^^;
q0, q1...등은 위의 상태0, 상태1등과 같습니다.

상태집합      : 여기서의 목적은 SEARCH를 DEST에서 찾는 것이 목적입니다.
                0은 초기화상태 또는 하나도 맞지 않는 상태이고, 1은 하나
                의 글자가 맞는 상태입니다. 이 상태의 갯수는 SEARCH의 길
                이 + 1 입니다. 상태가 6이 되면 6개의 글자가 다 맞는 것이
                되고 종료해야 겠지요?
초기상태      : 당연히 0으로 초기화됩니다.
종료상태      : 6개 글자가 다 맞으면 그 위치를 돌려주면 되겠죠?
입력문자집합  : 여기서는 '0', '1'로 2개로 단순화 했습니다.
상태전이 함수 :

"110101"을 찾는다고 했으니 검색하기 전에는 상태는 0이 된다고 했습니다.
대상문자열중에서 "1"이 나온다고 하면 찾는 문자열의 앞부분과 하나가 맞으
니 상태는 1로 진전됩니다. 다시 "1"이 나오면 2개가 맞으니 상태가 2로 됩
니다. 만일 "0" 이 나온다면 "10"이므로 찾는 문자열의 앞부분과 일치되지
않으므로 상태 0이 되는 것입니다. 상태2에서 "0"을 만나면 상태3이 되고 ,
다시 "1"을 만나면 상태4가 되겠죠? 이상태에서 다시 "0"을 만난다면 문제없
이 상태5로 되겠지만 "1"을 만난다면 그간 비교된 문자는 "11011" 이 되어서
뒤의 2개의 문자가 SEARCH의 "110101"의 앞의 두개문자와 같으므로 상태는 2
가 됩니다. 이런 식으로 검색하여 상태가 6이 된다면 "110101"이 다 맞는 것
이 되어서 리턴을 하는 것이 오토마타를 사용한 문자열 검색법입니다.
자세히 보시면, 오토마타는 순전히 SEARCH에 의존합니다. 오토마타가 완성될
상태를 0에서 N개 까지 만들어 놓고, 각 들어올 수 있는 경우(여기서는 0,1)
에 따라 SEARCH를 참조하여 상태를 전진시키거나 후퇴시키는 것입니다.
만일 아스키문자를 비교한다고 하면 입력문자집합은 256개가 되어서 거대한
표나 상태전이함수를 만들어 둬야 합니다. 같은 문자열을 계속 찾는 경우에
는 아주 유용한 알고리즘이지만 SEARCH가 자주 바뀌면 바뀔 때 마다 상태
전이함수를 만들어야 하기 때문에 리소스의 낭비가 심해지게 됩니다.

"오토마타를 이용한 문자열 검색 알고리즘"의 의사코드는 다음과 같이 됩
니다.

S = "001011010101"
P = "110101"

-----------------------------------------------------------------------

q = 0;               /* 상태를 0으로 초기화 */
for(i = 0; i< strlen(S); i++) { /* 문자열의 끝까지 비교 */
        q = (q, S[i]);                  ----------- (1)
        if (q == strlen(P)) return i-strlen(P)+1; ------ (2)
}
return -1;

-----------------------------------------------------------------------

(1)에서 괄호안의 q는 현재 상태이고, S[i]는 S에서 현재 비교되는 문자입니
다. 이 두값을 가지고 [표 2]에서 작성한 상태전이함수에서 다음의 상태를
가져옵니다. (2)에서 상태가 P의 길이인 6이 되면 다 맞는 결과이므로 현재
위치(i--배열첨자식)에서 P의 길이를 빼서 1을 보내면 S에서 P의 문자열이 나
타나는 처음 위치입니다.

전이함수는 실제로는 2차원 배열입니다.

#define NSIGMA 2
char DELTA[6][NSIGMA];

앞의 6은 strlen(P)인 상태의 갯수인데, [표 2]와 비교해 보시면 제일 마지막
의 상태6은 실제로없어도 되는 부분이므로 배열 6개만 잡아도 충분합니다.
뒤의 NSIGMA는 입력문자의 갯수입니다. 여기서는 '0', '1'이 올 수 있기 때문
에 2로 정의하였습니다. 실제로 이 배열은 [표 2]를 배열로 표현한 것입니다.
즉, '0', '1'을 배열의 첨자로 사용하기 위해 0, 1로 순서를 매긴다고 보면,
"DELTA[3][1]"은 상태3에서 '1'을 만났을 때를 이야기 하며 그 값은 4가 되어
야 합니다.
이제 전이함수인 2차원 배열의 값을 어떻게 구할 것인가 생각해봅시다.

P = "110101"
char pq[50], pj[50];
int q,j;

1) q = 0에서 시작한다.
2) P에서 q의 길이만큼 pq에 복사한다. (pq = "")
3) pq에 입력문자집합에 있는 것을 하나씩 pq의 뒤에 추가한다.
    (pq = "0")
4) j = strlen(pq);   (j = 1)
5) P에서 j만큼 pj에 복사한다. (pj = "1")
6) pj가 pq의 접미어인가? 즉, pj의 문자열이 pq의 뒤에 나타나는가?
   아니면 --j만큼 P에서 pj로 문자열을 복사해서 계속 검사한다.
   ("1"이 "0"의 접미어? 아니다. 따라서 pj 는 이번에는 ""이 된다. j = 0
    이 되고..""은 "0"의 접미어? )
7) DELTA[q][i] = j;  (DELTA[0][0] = 0)
8) 2)번으로 가서 다시 반복하고 q = 1로 해서 다시 반복한다.

이해가 되는가요?
원리는 간단합니다. P에서 0개부터 5개까지 문자열을 가져와서 나올 수 있는
입력문자를 하나씩 붙여서 이것이 P에서 1개에서 6개 까지의 문자가 접미어가
되는가를 검사하는 것입니다. 곰곰히 생각해 보시길..

-----------------------------------------------------------------------

int q;        /* 상태 */
int i, j;
int pl;       /* 문자열 P의 길이 */
char pq[50], pj[50]; /* 문자열을 저장할 임시공간 */

pl = strlen(P);
for (q = 0; q < strlen(P); q++) { /* strlen(P) 는 6 */
   for (i = 0; i < NSIGMA; i++) { /* 2차원 배열에 값을 구함 */
      substr(P, pq, q);   -- (1)
      pq[j = strlen(pq)] = table[i];  -- (2)
      pq[++j] = '\0';                 -- (3)
      while (!is_suffix(substr(P, pj, j), pq)) j--;  -- (4)
      DELTA[q][i] = j;               -- (5)
   }
}

-----------------------------------------------------------------------

2차원 배열 DELTA에 값을 얻기 위해서 2중 루프를 돌린다는 것은 쉽게 이해가
갈 것입니다. 여기에서 substr(P, pq, q)함수는 간단히 만든, 문자열 P에서 pq로
q의 길이만큼 복사하는 함수입니다. is_suffix(a, b)는 a가 b의 접미어이면 1을
돌려주고 아니면 0을 돌려주며 나올때까지 j를 감소시킵니다.

------------------------------------------------------------------------
char *substr(char s[], char t[], int k) {
        int i;
        for (i = 0; i < k; i++)
            t[i] = s[i];
        t[i] = '\0';
        return t;
}

int is_suffix(char p[], char s[]) {
   int pl, sl, i, j;
   pl= strlen(p);
   sl= strlen(s);
   if (pl > sl) return 0;
   for (i = pl -1, j = sl -1; i >= 0; i--, j--)
    if (p[i] != s[i]) return 0;
   return 1;
}
------------------------------------------------------------------------

상당히 어설프게 설명을 한 것 같습니다. 하지만 잘 살펴보시면 이해가 가실
겁니다. 굳이 뒷부분은 이해가 안되신다 하더라도 오토마타 원리부분만 이해
를 하셨더라도 다음으로 넘어가실 수 있을 겁니다. 이 부분을 설명드린 것은
어디까지나 오토마타라는 알고리즘에 대해서 말씀드리기 위한 것입니다.

이제 위의 풀 소스를 보도록 합시다.

------------------------------------------------------------------------
/* 
  automata.c -- 
    오토마타를 사용한 문자열 검색 
    검색대상 문자열에서 하나의 문자를 검색해 나갈때 마다 미리 만들어진
    오토마타 테이블의 값을 참조하면서 비교 

  modified by Han donghun . 1997.1.16.
*/

#include <stdio.h>
#include <string.h>

#define MAX_LEN  50
#define NSIGMA  2

char DELTA[6][NSIGMA];
char table[NSIGMA] = {'0','1'};

/* p[] 가 s[]의 뒷부분과 일치하면 1을 되돌리고, 아니면 0을 되돌린다.*/
int is_suffix(char p[], char s[]) {
  int pl, sl;
  int i, j;

  pl = strlen(p);
  sl = strlen(s);
  if (pl > sl) return 0;
  for (i = pl - 1, j = sl - 1; i >= 0; i--, j--)
    if (p[i] != s[j]) return 0;
  return 1;
}

/* 문자열 s[]의 처음부터 k개를 t[]로 복사함 */
char *substr(char s[], char t[], int k) {
  int i;

  for (i = 0; i < k; i++)
    t[i] = s[i];
  t[i] = '\0';
  return t;
}

/* 전이 함수를 만드는 함수 */
void make_transition(char p[], char delta[][NSIGMA]) {
  int q;  /* 상태 */
  int i, j;
  int pl; /* p 문자열의 길이 */
  char pq[MAX_LEN], pj[MAX_LEN]; /* 부분 문자열을 저장할 임시공간 */

  pl = strlen(p);
  for (q = 0; q < pl; q++) 
    for (i = 0; i < NSIGMA; i++) {
      substr(p, pq, q);   /* p[0..q]a 를 만드는 과정 */
      pq[j = strlen(pq)] = table[i];
      pq[++j] = '\0';
      /* 최대의 접미어가 되는 j+1을 찾음 */
      while (!is_suffix(substr(p, pj, j), pq)) j--;
      delta[q][i] = j; /* 전이 함수 설정 */
    }
}

int automata_strsch(char s[], char p[]) {
  int pl, sl;
  int i;
  int q = 0;

  pl = strlen(p);
  sl = strlen(s);
  make_transition(p, DELTA);
  for (i = 0; i < sl; i++) {
    q = DELTA[q][s[i] - '0'];
    if (q == pl) return i-pl+1;
  }
  return -1;
}

void main() {
  char *search = "110010";
  char *src = "11000110011010110010";
  int index;

  index = automata_strsch(src, search);
  printf("string : %s\nsearch string : %s\nfind index = %d\n", 
            src, search, index);
  printf("answer string : %s\n", src + index);
}

----------------------------------------------------------------------

테스트 해보시면 잘 동작하는 것을 볼 수 있을 겁니다. 


다음 시간에는 오토마타를 사용한 한글입력기를 간략히 살펴보겠습니다.
그럼.

ddoch 한동훈


#1810   시삽    (김성대  )
[강좌] 오토마타 이야기 (3)                   0               05/14 14:05    39

5. 한글과 오토마타

어설픈 설명이지만 저번 강좌까지 어느 정도 이해를 하셨다면 한글입력에 사용
되는 오토마타를 이해한다는 것은 이제 식은 죽 먹기라는 것을 먼저 말씀드립
니다. 한글입력 방법에는 2벌식과 3벌식이 있는 데, 주로 많이 쓰이는 2벌식
을 위주로 말씀드리겠습니다. 2벌식을 이해하면 3벌식도 무척이나 쉽습니다.
하지만 진보적 기능을 추가하려면 조금 복잡해진다는 것을 염두에 두시길..

지금 부터는 "컴퓨터 속의 한글"에 나오는 방식을 사용하겠습니다. 비교적
기본적이고 괜찮은 것 같습니다.
3벌식은 입력되는 글쇠가 자판에 초성, 중성, 종성이 구별되어 있으나, 2벌식
은 구별되어 있지 않고, 'ㄱ'이라도 초성도 될 수 있고, 종성도 될 수 있습니
다. 따라서 2벌식에서는 입력되는 글쇠가 자음인지 모음인지가 중요해집니다.
그래도 일단 최종적으로는 초성, 중성, 종성을 조합하여야 하는 관계로 일단
가상 한글 낱자모 코드를 만들어 두는 것이 편리합니다.

[표 3]
-----------------------------------------------------------------------
   초    성                 중        성               종         성
-----------------------------------------------------------------------
   82          ㄱ            A3        ㅏ              C2         ㄱ
   83          ㄲ            A4        ㅐ              C3         ㄲ
   84          ㄴ            A5        ㅑ              C4         ㄱㅅ
   85          ㄷ            A6        ㅒ              C5         ㄴ
   86          ㄸ            A7        ㅓ              C6         ㄴㅈ
   87          ㄹ            AA        ㅔ              C7         ㄴㅎ
   .............       ...............           ...............
----------------------------------------------------------------------
[출전 : 컴퓨터속의 한글 317쪽]  16진수 표기

(위에서 중성인 경우에는 A7에서 AA로 뛰어버리는 데 아래의 "상용 조합형
한글 코드표" 에서 해당 글자가 없기 때문입니다. )

구체적인 상세한 내용은 직접 다 적어두지는 않겠습니다. 강좌의 목적은 모든
것을 다 설명하자는 것이 아니라 핵심적이고 이해가 잘 되지 않는 부분들만
해설하자는 것이므로 자세한 사항은 해당 자료를 보시기 바랍니다.

아마도 한글출력기를 완성해 보신 분이라면 "상용 조합형 한글 코드표"를 마르
고 닮도록 보셨을 겁니다. 아래처럼 생긴 것입니다. 위의 가상코드표는 편의
적으로 만들어 진 것이지만, 아래의 코드표는 규약입니다.

[표 4]
-----------------------------------------------------------------------
     코   드           초  성         중   성             종 성
-----------------------------------------------------------------------
  0 / 00000b             .               .                  .
  1 / 00001b   채움            .                 채움
  2 / 00010b             ㄱ              채움               ㄱ
  3 / 00011b             ㄲ              ㅏ                 ㄲ
  4 / 00100b             ㄴ              ㅐ                 ㄱㅅ
  ...........         ...........      ..........         ...........
-----------------------------------------------------------------------
[출전 : 컴퓨터속의 한글 123쪽]

위와 마찬가지로 "ㄱㅅ" 같은 것은 쌍종성인데, 글자 입력이 곤란한 관계로
띄워져 있습니다.
예전에 SVGAlib 강좌를 하면서 한글출력의 기본적인 사항만 말씀드린 적이
있는 데, 다시 한번 말씀드리겠습니다. 한글은 기본적으로 2바이트로 이루어
지며 첫바이트는 0x80이상입니다. 0x80, 즉 아스키코드 128 미만은 일반적인
영문자를 표현하는 데 사용됩니다. 비교적 쓰이지 않는 128 이상을 한글의
첫바이트로 하고 둘째바이트는 무조건 한글 둘째바이트로 취급을 합니다.
앞서도 말씀드렸지만 참고로, 완성형은 둘째바이트도 128 이상이나 조합형은
둘째바이트가 128미만으로 'a'와 같은 영문자 일수도 있습니다. 이것은 조합
형의 원리에서 야기되는 문제로 조금 골치아픈 문제를 야기시킬 수도 있습니
다. '각'이라는 글자를 조합형으로 쳐놓고 바이너리 에디터로 살펴보면,

   88   62       (16진수)

일반적으로 한글에디터에서 한글임을 인식하는 방법은 처음부터 읽어나가다
가 0x80 이상 되면 한글로 취급하고 다음 바이트는 검사 할 필요도 없이 둘
째 바이트로 취급합니다. 완성형은 그냥 이 값을 가지고 코드표에서 찾아오
면 그만이지만 조합형은 초성값, 중성값, 종성값으로 분리를 해서 각각의
글자모양을 찾아와서 조립을 해야 합니다.

0x88 <<8 + 0x62  = 0x8862

일단 먼저 이렇게 앞바이트를 8비트 위로 밀어버리고 뒤의 바이트를 보내서
코드 조립을 합니다. 다음 분리를 합니다.

0x8862  =  1000 1000 0110 0010 b

최상위 비트는 0x80값이므로 한글임을 구별하는 비트입니다. 일단 이것은 제
외하고 나머지 15개 비트를 다섯개씩 쪼개서 초성, 중성, 종성 코드로 삼는
것입니다.

최상위    초성    중성    종성
비트

  1       00010   00011  00010      = 0x8862

           ㄱ       ㅏ     ㄱ       =  각

방금 위에서 예를 들은 "상용 조합형 한글코드표"에서찾아보시면 쉽게 답이
나옵니다.
그럼, [표 3]과 [표 4]의 차이점은 무엇인가에 대해서 잠시 이야기를 하겠습
니다. [표 3]은 임의적으로 만든 표이긴 하지만 나름대로의 깊은 뜻이 있습
니다. [표 3]의 "한글 낱자모 코드"는 [표 4]의 "상용 조합형 한글 코드표"
에 초성은 0x80, 중성은 0xA0, 종성은 0xC0을 각각 더한 것입니다. 이렇게
[표 3]을 만든 것은 2가지 이유가 있어 보입니다.

첫째는, 한글입력에서의 문제입니다. 애시당초 컴퓨터에서는 다중 바이트
언어권을 위한 배려가 없는 관계로 소프트웨어 적인 측면이나 한글카드를
사용할 경우를 제외한다면, 한글입력 이라는 것은 개념적인 이야기 일 뿐
입니다. 어떤 이야기냐 하면, 'a'를 키보드에서 누를 때, 키보드는 'a'에
해당하는 스캔코드와 아스키코드를 발생시킬 뿐이지 'ㅁ'으로 인식하는 것
은 해당 한글 입력기의 역할이라는 것입니다. 즉, 프로그램 내부적으로 설
정하거나 사용자가 변경할 수 있는 [영문],[한글] 상태에 따라 입력기에서
는 영문자로 처리하느냐 한글낱자로 처리하느냐를 결정하는 것입니다. 따라
서 입력기에서는 'a', 'Z'등과 같은 128미만의 영문자 아스키코드와 한글낱
자를 구분하기 위해서 한글낱자에게도 128이상의 값들을 부여해주는 것이 편
리합니다. [표 3] 에 보시면 모든 한글낱자의 가상코드값이 128이상인 것을
볼 수 있습니다.
둘째는, 초성, 중성, 종성 이나 자음, 모음 임을 가리기 위한 비트연산에서
구별을 편리하게 하고자는 의도도 있는 것으로 보입니다.

초성 : 0x82              1000 0010b
       ....              .......
       0x94              1001 0100b

중성 : 0xA3              1010 0011b
       ....              .......
       0xBD              1011 1101b

종성 : 0xC2              1100 0010b
       ....              .......
       0xDD              1101 1101b

초성, 중성, 종성의 6, 5번째 비트값이 각각 00, 01, 10으로 다 다르기 때문에
0x60 ( 0110 0000b)로 &연산을 하면 6, 5번째 비트는 00, 01, 10으로 나오는데,
그 값은 0x00, 0x20, 0x40으로 되어 초성, 중성, 종성임을 아주 편리하게 알아
낼 수 있다는 것입니다.

이제 글쇠입력에서 한글을 처리하는 방법을 알아보도록 하겠습니다. 
테이블이 하나 필요합니다. 이부분은 소스파일에 들어가는 일반적인 배열입니
다. 일단 한번 보시죠..

/* --------------------------------------------------------------------- 
   두벌식 한글 자판 배열 표 
   한글입력상태라도 영어대문자에 해당하는 글쇠를 누를 경우 대응하는
   한글글쇠가 없을 경우 그냥 영문코드를 돌려준다. 
   0x20(공백글쇠) 부터 인덱스를 시작한다. 
--------------------------------------------------------------------- */
static byte hanKbdTable2[] = {
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,  /*  !"#$%&' */
  0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,  /* ()*+,-./ */
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,  /* 01234567 */
  0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,  /* 89:;<=>? */
  0x40, 0x41, 0x42, 0x43, 0x44, 0x86, 0x46, 0x47,  /* @ABCDEFG */
  0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0xA6,  /* HIJKLMNO */
  0xAC, 0x8A, 0x83, 0x53, 0x8C, 0x55, 0x56, 0x8F,  /* PQRSTUVW */
  0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,  /* XYZ[\]^_ */
  0x60, 0x88, 0xBA, 0x90, 0x8D, 0x85, 0x87, 0x94,  /* `abcdefg */
  0xAD, 0xA5,0xA7, 0xA3, 0xBD, 0xBB, 0xB4, 0xA4,  /* hijklmno */
  0xAA, 0x89, 0x82, 0x84, 0x8B, 0xAB, 0x93, 0x8E,  /* pqrstuvw */
  0x92, 0xB3, 0x91, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F   /* xyz{|}~  */
};
-----------------------------------------------------------------------

유심히 쳐다보면 알듯알듯 하면서도 잘 이해가 안되실 겁니다. 저는 이것을
이해하는 데 20분이 걸리더군요. 여러분들은 2분만으로 충분하실 겁니다.
일단 배열크기는 8x12 = 96개 입니다. 즉, 일반적으로 128 미만의 아스키문자
중 키보드로 타이핑 할수있는 것은 다 나왔다고 보시면 됩니다. 인덱스는
아스키코드대로 순서대로 되어 있으며 공백글쇠부터 시작합니다. 자, 이제 
아스키코드표를 어디서 구해서 맞춰봅시다. 눈동작만으로도 이해하실 수 있
으신 분은 그냥 보셔도 되겠습니다.
일단은 글쇠가 들어오면 [한글], [영문] 입력상태를 채크합니다. 한글영문
상태는 전역변수로서 보통 선언하며 0은 영문, 1은 한글로 정의하며 영문으로
초기화를 많이 합니다. 자, 이제 영문입력모드라고 가정합니다. 그래서 
"linux"라고 타이핑하고 싶어서 글쇠를 하나씩 쳤습니다. 사용자 함수에서는
현재 영문입력모드므로 한글로 변환할 필요없이 그냥 아스키코드를 반환해서
출력루틴을 불러서 찍어버리면 그만입니다. 그런데 이제는 마음이 변해서
"리눅스"라고 타이핑을 하겠다고 한글입력모드로 바꾼뒤 입력한다고 가정
합시다. 일단 'f'글쇠를 쳐서 'ㄹ'을 입력할 것입니다. 사용자함수에서는
현재 입력모드가 한글모드이므로 입력받은 영문글쇠값을 가지고 위의 표에
서 검사를 합니다. 'f'에 해당하는 것을 한번 찾아봅시다. 0x87값임을 금
방 찾을 수 있습니다. 이 값을 가지고 표3에서 한번 찾아봅시다. 'ㄹ'임을
알 수 있습니다. 이제 이값을 가지고 2벌식에서는 자음인지, 모음인지..
3벌식에서는 초성, 중성, 종성 중 어느것인지 판별하기 위해서 글쇠값에다
0x60을 &연산합니다. 이제 자음인지 모음인지도 알아내었으니 오타마타를
불러서 조립만 하면 됩니다. 
(즉, 위의 테이블은 한글입력모드일때의 영문키에 해당하는 한글 낱자입니
다. 물론 2벌식에서 말입니다. 나중에 원코드를 찾기위해서 [표 4]로 달려
가기전에 글쇠값에서 초성은 0x80을 뺍니다. 중성, 종성도 마찬가지로 앞
에서 더한 값을 뺍니다. )

잠깐 키를 받아들이는 부분을소스로 볼까요? 제가 부분적으로 "컴퓨터속의
한글"에 나오는 소스를 수정한 것입니다. 

-------------------------------------------------------------------------
#include <stdio.h>
#include "hangul.h"

static int pushedKey = 0; /* 읽어온 키나 끄집어낸 키를 저장하는 곳 */

/* --------------------------------------------------------------------- 
   두벌식 한글 자판 배열 표 
   한글입력상태라도 영어대문자에 해당하는 글쇠를 누를 경우 대응하는
   한글글쇠가 없을 경우 그냥 영문코드를 돌려준다. 
   0x20(공백글쇠) 부터 인덱스를 시작한다. 
--------------------------------------------------------------------- */
static byte hanKbdTable2[] = {
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,  /*  !"#$%&' */
  0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,  /* ()*+,-./ */
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,  /* 01234567 */
  0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,  /* 89:;<=>? */
  0x40, 0x41, 0x42, 0x43, 0x44, 0x86, 0x46, 0x47,  /* @ABCDEFG */
  0x48, 0x49, 0x4A, 0x4B,0x4C, 0x4D, 0x4E, 0xA6,  /* HIJKLMNO */
  0xAC, 0x8A, 0x83, 0x53, 0x8C, 0x55, 0x56, 0x8F,  /* PQRSTUVW */
  0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,  /* XYZ[\]^_ */
  0x60, 0x88, 0xBA, 0x90, 0x8D, 0x85, 0x87, 0x94,  /* `abcdefg */
  0xAD, 0xA5, 0xA7, 0xA3, 0xBD, 0xBB, 0xB4, 0xA4,  /* hijklmno */
  0xAA, 0x89, 0x82, 0x84, 0x8B, 0xAB, 0x93, 0x8E,  /* pqrstuvw */
  0x92, 0xB3, 0x91, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F   /* xyz{|}~  */
};
/* --------------------------------------------------------------------- 
   세벌식 한글 자판 배열 표 
   초성, 종성이 서로 독립적인 코드를 가진다. 
--------------------------------------------------------------------- */
static byte hanKbdTable3[] = {
  0x20, 0xD8, 0x22, 0x23, 0x24, 0x25, 0x26, 0x92,  /*  !"#$%&' */
  0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0xAD,  /* ()*+,-./ */
  0x91, 0xDD, 0xD6, 0xD3, 0xB3, 0xBA, 0xA5, 0xAC,  /* 01234567 */
  0xBC, 0xB4, 0x3A, 0x89, 0x32, 0x3D, 0x33, 0x3F,  /* 89:;<=>? */
  0x40, 0xC8, 0x21, 0xCB, 0xCA, 0xDA,0xC3, 0x3B,  /* @ABCDEFG */
  0x27, 0x38, 0x34, 0x35, 0x36, 0x31, 0x30, 0x39,  /* HIJKLMNO */
  0x3E, 0xDC, 0xA6, 0xC7, 0x3A, 0x37, 0xD0, 0xDB,  /* PQRSTUVW */
  0xD4, 0x3C, 0xD9, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,  /* XYZ[\]^_ */
  0x60, 0xD7, 0xB4, 0xAA, 0xBD, 0xAB, 0xA3, 0xBB,  /* `abcdefg */
  0x84, 0x88, 0x8D, 0x82, 0x8E, 0x94, 0x8B, 0x90,  /* hijklmno */
  0x93, 0xD5, 0xA4, 0xC5, 0xA7, 0x85, 0xAD, 0xC9,  /* pqrstuvw */
  0xC2, 0x87, 0xD1, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F   /* xyz{|}~  */
};

/*버퍼에 입력된 키가 있으면 그 값을 돌려주고 없으면 0을 돌려준다. */
int hinkeybuf(void) {
  return pushedKey ? pushedKey : 0;
}

/* 중간 저장고에 키를 밀어넣는다. */
void hputkey(int key) {
  pushedKey = key;
}

/* 입력된 값으로 한글값이나 여타의 키로 변환한다. */
int hconvertkey(int key) {
  byte *hanKbdTable; /* 두벌식 또는 세벌식 한글 자판 배열표를 가르킬 포인터 */


  /* 한글입력상태가 아니거나 특수제어키나 확장키일 경우 그냥 반환 */
  if (hanMode[HAN_ENG_MODE] == ENG_INPUT || key <= 32 || key > 127)
    return key;
  /* 두벌식, 세벌식 테이블을 참조하여 해당인덱스의 코드를 반환 */
  hanKbdTable = (hanMode[HAN_JAPAN] == DUBEL_TYPE) ? hanKbdTable2 : hanKbdTabl
e3;
  return hanKbdTable[key-32];
}

/* 키 입력을 받는다. */
int hgetkey() {
  int key;

  if (pushedKey) { /* 중간 저장고에 키가 있다면 */
    key = pushedKey; /* 그 값을 가져오고 0으로 설정한다. */
    pushedKey = 0;
  } else {
    key = hgetch(); /* 저장고에 키가 없다면 새로 입력을 받는다. */
    /* 한글입력을 받는다면키값을 그에 맞게 변환하고 아니면 그냥 반환한다. */
    key = hconvertkey(key);
  }
  return key;
}

-----------------------------------------------------------------------------
주석이 있으니 쉽게 이해가 갈것입니다. 그러니까 hgetkey()가 여기서는 제일
아래에서 글을 입력받습니다. hgetch()는 제가 SVGAlib에서 통일적으로 글쇠
를 받고자 만든 함수입니다. hputkey(key)는 key를 저장고에 밀어놓기 위한
것인데, 이유는 한글조립시 '갑'이라는 글자가 들어올때 여기까지도 완성된
글이라 보기 어려운데, 'ㅅ'이 하나 더 들어와서 쌍종성을 이룰 수 도 있기
때문이며 'ㅡ'가 들어올경우 '갑'의 'ㅂ'을 빼서 다음 글자로 'ㅡ'와 같이
넘겨주기 위해서 필요한 저장고 입니다. hanMode[3]은 제가 만들어둔 일상
적인 '영문 한글 토글', '2벌 3벌토글', '조합완성토글'을 담는 상태배열
입니다. 그리고 2벌식인지 3벌식인지도 참조해서 해당 테이블에서 값을 가
져와야 합니다. key에서 32를 뺀것은 위에서도 설명했듯이 hanKbdTable배
열이 공백글쇠(32)를 0으로 인덱스화 했기 때문입니다. 

여기까지가 한글낱자를 입력받는 부분입니다. 이제부터 간단하게 한글낱자를
조립하는 오토마타를 선을 보이겠습니다. 미리 양해를 구해야 할 사항은
텍스트환경이라 그림을 잘 그릴 수 없는 관계로 미려한 그림은 해당 자료를
참조하시기 바랍니다. 앞강좌에서 오토마타를 어느정도 이해를 하셨다면
한글오토마타에서 어떻게 활용되는지 쉽게 이해하실 수 있습니다.

2벌식의 한글조립상태 

enum HAN_STATUS {
  HS_START, 
  HS_CHOSUNG,
  HS_JOONGSUNG, HS_DJOONGSUNG,
  HS_JONGSUNG, HS_DJONGSUNG,
  HS_END1, HS_END2
};

2벌식에서 왜 상태가 8개가 되는 지 간단히 먼저 보죠.
실제로 조립되는 예만 한두어개 생각해보면 간단합니다.

'왔' -- 초성('ㅇ')하나, 중성('ㅗ','ㅏ')2개, 종성('ㅅ','ㅅ')2개

최대로 많이 조합될 수 있는 경우입니다. 2벌식에서는 보편적으로 초성을
두번으로 나누어 입력할 수 없습니다. 표준적인 경우에는 그렇지만 아래아
한글 같은 경우는 오토마타를 초성을 두단계로 쪼개기도 하죠. 즉, 'ㄱ',
다음에 'ㄱ'을 또 입력시키면 'ㄲ'으로 인식하기도 합니다. 

그런데 왜 END가 붙은 게 두개인가.. 이문제는 2벌식의 특성때문에 그렇
습니다. 앞서 예를 든 글자 "갑"을 현재 조립하고 있다고 했을때 'ㅎ'이
들어올지 'ㅡ'가 들어올지 모르는 상태입니다. 'ㅎ'이 들어오면 이것은 '갑'
글자의 종성에 추가가 될수 없는 글자입니다. 따라서 스택을 통하여 'ㅎ'하
나를 다음 글자조립을 위해서 넘겨줍니다. 'ㅡ'가 들어올경우에는 문제가 달
라집니다. 즉, '가브'가 형성됩니다. 즉, '갑'에서 'ㅂ'을 빼오고 'ㅡ'까지
하나 더 스택에 넘겨주게 됩니다. 즉, 2개를 넘겨줍니다. 이렇게 다음글자로
넘겨주는 글자갯수가 2벌식에서는 다르기 때문에 종료상태가 2개로 되는 것
입니다. 잠깐 3벌식의 경우를 살펴보면 초성, 중성, 종성이 명확히 분리가
되어 있는 관계로 '갑'다음에 'ㅡ'가 오더라도 '갑'의 'ㅂ'을 빼가지 않습
니다. 'ㅂ'은 종성이지 초성이 아니기 때문이죠. 따라서 3벌식에서는 종료
상태가 하나만으로도 충분합니다.

이제 몇가지 대표적인 글자를 입력받을 경우에 한글조합상태가 어떻게 변
하는 지 살펴보겠습니다.

'리눅스'

------------------------------------------------
입력된 낱자   조합된 글자      한글조합상태
------------------------------------------------
                                 HS_START
'ㄹ'           'ㄹ'              HS_CHOSUNG
'ㅣ'           '리'              HS_JOONGSUNG
'ㄴ'           'ㄴ'              HS_CHOSUNG
'ㅜ'           '누'              HS_JOONGSUNG
'ㄱ'           '눅'              HS_JONGSUNG
'ㅅ'           'ㅅ'              HS_CHOSUNG
'ㅡ'           '스'              HS_JOONGSUNG
------------------------------------------------

한글낱자입력에 따른 한글조합상태의 변화를 너무나도 쉽게 알수 있을 것
입니다. 한글오토마타는 저번강좌에서 설명한 것과 같은 2차원 상태변이
함수로 처리하는 것보다는 간단히 각경우에 따른 CASE문을 사용하면 쉽
게 처리할 수 있습니다. 

(하이텔이 500라인이 한계인데 소스를 올릴려니 안되겠네요. 다음편으로
넘겨야 할 것 같습니다.  ^^)


#1811   시삽    (김성대  )
[강좌] 오토마타 이야기 (4) 마지막            0               05/14 14:05    39

--------------------------------------------------------------------------
        2   벌 식       한글 오토마타 
--------------------------------------------------------------------------

#include <stdio.h>
#include "hangul.h"

#define CONSONANT 0
#define VOWEL 1

enum HAN_STATE {
  HS_START,
  HS_CHOSUNG,
  HS_JOONGSUNG, HS_DJOONGSUNG,
  HS_JONGSUNG, HS_DJONGSUNG,
  HS_END1, HS_END2
};

/* ---------------------------------------------------------------------
   HS_END1과 HS_END2가 다른 점은 HS_END2는 한글낱자가 조합되고 있는 과
   정에 모음이 들어왔을 때는 이전에 들어온 것 중에서 제일 마지막에 입력
   된 것이 자음이라면 자음을 하나빼내서 다음 글자로 넘겨줘야 하기 때문
   에 현재 입력된 글자와 빼낸 글자 2개를 넘겨줘야한다. 
   반면에 HS_END1은 글자가 조합되고 있는 과정에서 이전에 입력된 것이
   모음일 경우 모음이 다시 들어 오더라도 이전 모음을 빼낼 이유가 없으
   니 새로 들어온 모음 하나만 다음글자로 넘기면 된다. 그리고 더이상 조
   합될 수 없는 자음이 들어올 경우에는 그 자음 하나만 다음글자로 넘기
   면 된다. 따라서 HS_END2는 글자2개를 출력스택에 넘기고 HS_END1은 하나
   만 출력스택에 넘기는 점에서 다르게 작동한다.  
--------------------------------------------------------------------- */

static int  tempUnionCode;
static int   oldKey;
static int  keyCode;

/* 겹모음을 이루는 지를 검사한다. 찾았으면 해당하는 코드를 돌려주고
   아니면 0을 돌려준다. */
static int joongsungPair(void) {
  static byte dJoongTable[7][3] = {
    { 0xad, 0xa3, 0xae },    /* ㅗ, ㅏ, ㅘ */
    { 0xad, 0xa4, 0xaf },    /* ㅗ, ㅐ, ㅙ */
    { 0xad, 0xbd, 0xb2 },    /* ㅗ, ㅣ, ㅚ */
    { 0xb4, 0xa7, 0xb5 },    /* ㅜ, ㅓ, ㅝ */
    { 0xb4, 0xaa, 0xb6 },    /* ㅜ, ㅔ, ㅞ */
    { 0xb4, 0xbd, 0xb7 },   /* ㅜ, ㅣ, ㅟ */
    { 0xbb, 0xbd, 0xbc }    /* ㅡ, ㅣ, ㅢ */
  };
  int i;

  /* 이전에 입력된 키와 현재 입력된 키를 겹모음 테이블에서  */
  /*   검색한다.                                            */
  for (i = 0; i < 7; i++) {
    if (dJoongTable[i][0] == oldKey && dJoongTable[i][1] == keyCode)
      return ((keyCode = dJoongTable[i][2]));
  }
  return 0;
}

/* 겹받침을 이루는 지 검사한다.  찾았으면 해당하는 키를 돌려주고 
   아니면 0을 돌려준다. */
static int jongsungPair(void) {
  static byte dJongTable[11][3] = {
    { 0x82, 0x8b, 0xc4 },    /* ㄱ, ㅅ*/
    { 0x84, 0x8e, 0xc6 },    /* ㄴ, ㅈ*/
    { 0x84, 0x94, 0xc7 },    /* ㄴ, ㅎ*/
    { 0x87, 0x82, 0xca },    /* ㄹ, ㄱ*/
    { 0x87, 0x88, 0xcb },    /* ㄹ, ㅁ*/
    { 0x87, 0x89, 0xcc },    /* ㄹ, ㅂ*/
    { 0x87, 0x8b, 0xcd },    /* ㄹ, ㅅ*/
    { 0x87, 0x92, 0xce },    /* ㄹ, ㅌ*/
    { 0x87, 0x93, 0xcf },    /* ㄹ, ㅍ*/
    { 0x87, 0x94, 0xd0 },    /* ㄹ, ㅎ*/
    { 0x89, 0x8b, 0xd4 }    /* ㅂ, ㅅ*/
  };
  int i;

  /* 이전에 입력된 키와 현재 입력된 키를 겹받침 테이블에서 찾아서
     있는지를 검사한다. */
  for (i = 0; i < 11; i++) {
    if (dJongTable[i][0] == oldKey && dJongTable[i][1] == keyCode)
      return (keyCode = dJongTable[i][2]);
  }
  return 0;
}

/********************************************************************
                  두벌식 오토마타
********************************************************************/

int hanAutomata2(int key) { 

/*--------------------------------------------------------------------
인  수 : key = 입력된 키 코드
반환값 : 한 글자의 조합이 끝나면 1을, 계속 조합중이면 0을,
         완성된 글자의 코드는 입력 스택의 가장 마지막에서
         구할 수 있다. 
동작   : 
--------------------------------------------------------------------*/

  int chKind, canBeJongsung = FALSE;
  /* 초성코드에 대응하는 종성 코드 */
  static byte Cho2Jong[] = {  
    0xc2,  /*  기역                 */ 
    0xc3,  /*  쌍기역               */
    0xc5,  /*  니은                 */
    0xc8,  /*  디귿                 */
    0x00,  /*  쌍디귿 (해당 없음)   */
    0xc9,  /*  리을                 */
    0xd1,  /*  미음                 */
    0xd3,  /*  비읍                 */
    0x00,  /*  상비읍 (해당 없음)   */
    0xd5,  /*  시옷                 */
0xd6,  /*  쌍시옷               */
    0xd7,  /*  이응                 */
    0xd8,  /*  지읒                 */
    0x00,  /*  쌍지읒 (해당 없음)   */
    0xd9,  /*  치읓                 */
    0xda,  /*  키읔                 */  
    0xdb,  /*  티읕                 */
    0xdc,  /*  피읖區               */
    0xdd  /*  히읗                 */
  };

/* --------------------------------------------------------------------- 
   입력된 낱자가 어떤 형태인가를 규정하고 이전의 한글입력상태와 조합된
   코드를 얻어오는 오토마타의 작업준비 부분 
--------------------------------------------------------------------- */

  /* 글쇠가 모음일 경우에는  01100000b 로 &연산을 하면 00100000b가 된다. */
  if ((key & 0x60) == 0x20) {
    chKind = VOWEL;   /* 모음 */
  }  else {
    chKind = CONSONANT; /* 그 이외에는 자음 */
    /* 쌍디귿, 쌍비읍, 쌍지읒은 받침으로 올 수 없다. */
    if (!(key == 0x86 || key == 0x8A || key == 0x8F))
      canBeJongsung = TRUE;
  }
  /* 만일 한글이 입력되고 있다면 이전에 저장된 스택에서 조합된
     코드와 입력된 글쇠를 가져온다. */
  if (tempHanState) {
    tempUnionCode = inStack[inSP - 1].unionCode;
    oldKey = inStack[inSP - 1].key;
  }  else {
    /* 처음이라면..*/
    /* 0x8441은 각 초성, 중성, 종성의 채움문자의 OR연산결과임 */
    tempUnionCode = 0x8441;  
    oldKey = 0;
  }
  keyCode = key; /* 작업할 keyCode로 복사 */

/* --------------------------------------------------------------------- 
   이전의 한글이 조합된 상태 (unionCode 또는 tempUnionCode)와 현재 입력된
   글자가 어떤 형태인가에 따라 조합상태를 다시 결정짓는 오타마타 주요부분 
--------------------------------------------------------------------- */

  switch (tempHanState) {
    case HS_START : /* 한글입력이 처음이고 */
      if (chKind == CONSONANT) /* 자음이 들어왔다면 초성상태로 */ 
        tempHanState = HS_CHOSUNG;
      else                      /* 모음이라면 중성상태로 */
        tempHanState = HS_JOONGSUNG;
      break;
    case HS_CHOSUNG : /* 초성이 이미 입력이 되었고 */
      if (chKind == VOWEL) /* 모음이 들어왔다면 */
        tempHanState = HS_JOONGSUNG; /* 중성상태로 돌진!! */
      else
        /* 2벌식에는 복초성을 두번으로 나누어 입력할 수 없다. */
        /* 따라서 초성이 들어온 상태에서 초성이 다시 들어온다면 */
        /*   당연히 글자조합을 끝을 낸다. */
        tempHanState = HS_END1; 
      break;
    case HS_JOONGSUNG :   /* 중성이 이미 들어와 있고 */
      if (canBeJongsung)  /* 종성이 될 수 있는 놈이라면 */
        tempHanState = HS_JONGSUNG; /* 종성상태로 발전한다. */
      /* 중성이 들어왔다면 겹모음을 이룰 수 있는 지를 검사해서
         겹모음이 될 수 있으면 복중성상태로 간다. 이도 저도 아니
         면 끝으로 간다. */
      else if (joongsungPair()) 
        tempHanState = HS_DJOONGSUNG;
      else
        tempHanState = HS_END1;
      break;
    case HS_DJOONGSUNG : /* 겹모음을 이루었고 */
      if (canBeJongsung) /* 종성이 될 수 있는 놈이라면 */
        tempHanState = HS_JONGSUNG; /* 종성상태로 ... */ 
      else
      /* 모음이나 종성이 될 수 없는 자음은 겹모음상태에서 받아들일 */
        /* 수 없다.                                                  */
        tempHanState = HS_END1; 
      break;
    case HS_JONGSUNG : /* 종성을 이루고 있고 */
      /* 자음이 들어와서 겹자음을 이룰 수 있는 놈이라면   */
      /* 복종성상태로 간다.                               */
      if (chKind == CONSONANT && jongsungPair())
        tempHanState = HS_DJONGSUNG;
      /* 만일 모음이 들어온다면 끝2로 간다. */
else if (chKind == VOWEL)
        tempHanState = HS_END2;
      /* 복종성을 이룰 수 없는 자음이라면 끝으로 간다. */
      else 
        tempHanState = HS_END1;  
      break;
    case HS_DJONGSUNG :  /* 복종성을 이루고 있고 */
      if (chKind == VOWEL) /* 모음이 들어왔다면 당연히 끝2로 */
        tempHanState = HS_END2;
      else
        tempHanState = HS_END1; /* 그외에는 안보고도 끝1로 */
      break;
  } 
/* --------------------------------------------------------------------- 
   한글낱자를 조합해서 코드를 얻는 곳이다. 부분적으로 뒤에서는 스택조정
   도 한다. 
  
   초성코드에서 0x80을 빼면 순수한 조합형 초성코드가 나오는 데
   여기서 10비트를 왼쪽으로 밀어버리면 조합할 수 있는 상태가 된다.
   이전의 조합된 코드에서 1000 0011 1111 1111 을 &연산을 하면 초성
   부분을 깨끗이 지울 수 있다. 이 둘을 OR연산하면 원하는 조합된
   코드를 얻을 수 있다.                                         

   마찬가지로 중성에서 0xA0을 빼면 순수한 중성코드를 얻을 수 있는 데,
   여기서 왼쪽으로 5비트를 밀어버리면 당연히 조합가능한 중성코드가 
   나온다. 원래의 코드에 0xFC1F를 &연산한다는 것은 1111 1100 0001 1111
   로 중성부분을 깨끗이 지운다는 것이고 이 둘을 OR 연산하면 원하는
   코드를 얻는다. 

   종성에서는 조금 다르다. 2벌식에서는 초성, 종성이 따로 없으므로 
   자음은 모조리 초성으로 가정하고 종성에는 쌍디귿, 씽비읍, 쌍지읒
   만이 올 수 없다. 따라서 처음에 초성으로 들어왔으므로 초성에 대응
   하는 종성코드가 기술되어 있는 테이블에서 인덱스로 찾아야 한다.
   keyCode에서 0X82를 뺀다는 것은 순수한 초성코드에서 채움문자를 제외
   한 곳에서부터 인덱스를 메기겠다는 것이고 이것으로 테이블에 가서 똑
   같은 모양글자의 종성코드를 가져온다. 
   복종성은 이미 앞의 dJongTable에서 알맞는 종성코드로 변환되었으므로
   이런 과정이 필요없다. 그러고 난 다음에 0xC0을 빼서 순수 종성코드를
   얻고 원래의 조합된 코드에서 1111 1111 1110 0000 을 & 연산해서 종성
   코드부분을 깨끗이 해서 이둘을 OR연산한다. 
--------------------------------------------------------------------- */
  switch (tempHanState) { /* 한글 낱자가 입력이 되어 있을 경우에만 */
    case HS_CHOSUNG :     /* 초성코드 처리 */
      tempUnionCode = (tempUnionCode & 0x83FF) | ((keyCode - 0x80) << 10);
      break;
    case HS_JOONGSUNG :   /* 중성코드는 복중성과 같이 처리된다. */
    case HS_DJOONGSUNG :  /* 복중성 코드 처리 */
      tempUnionCode = (tempUnionCode & 0xFC1F) | ((keyCode - 0xA0) << 5);
      break;
    case HS_JONGSUNG :    /* 종성코드는 초성2종성테이블에서 변환되어야 한다. *
/
      keyCode = Cho2Jong[keyCode - 0x82];
    case HS_DJONGSUNG :   /* 복종성 및 종성 코드 처리 */
      tempUnionCode = (tempUnionCode & 0xFFE0) | (keyCode - 0xC0);
      break;
    case HS_END1 :       
      outStack[outSP++] = key; /* 현재낱자 하나만 다음으로 넘긴다. */
      return TRUE;
    case HS_END2 :
      /* 현재 낱자를 먼저 출력스택에 넣고 이전 글쇠를 꺼집어 내서 출력
         스택에 집어넣는다. 스택은 먼저 들어간 것이 나중에 나온다. */
      outStack[outSP++] = key; 
      outStack[outSP++] = oldKey;
      inSP--;     /* 입력스택에서 하나를 빼같으므로 스택포인터 조정 */
      return TRUE; /* 1은 완료되었음을 의미 */
  }
  inStack[inSP].currentHanState = tempHanState;
  inStack[inSP].unionCode = tempUnionCode;
  inStack[inSP++].key = key;
  return FALSE;  /* 아직 한글 조합중 */
}

------------------------------------------------------------------------

주석이 너무 많아서 오히려 어지러울 수도 있겠지만 잘 읽어보시면 이해가 
빨리 될 것입니다. 주석에서 너무 상세히 설명이 된 것 같아 부연 설명은
하지 않아도 될 것같습니다. 처음의 상태는 HS_START에서 시작해서 입력
되는 글쇠에 따라서 상태가 변화하며 조립을 다했으면 1을, 조립중이면
0을 반환합니다. 그리고 조립이 완료되었다면 출력스택에 HS_END1, HS_END2
에 따라서 1개 내지 2개의 낱자를 밀어넣습니다. 


7. 나오는 말

이상으로 부족한 설명이지만 오토마타에 대한 이야기를 마치도록 하겠습니다.
자세한 풀소스나 기능은 조만간에 정리하여 발표될 한글라이브러리 "달래"
를 참조하시기 바랍니다. 

(한글라이브러리를 만들고 나서는 C++과 본래의 과제인 시스템 프로그래밍을
좀더 심도깊게 공부해 볼 계획입니다. )

힘든 강좌를 따라오신다고 수고하신 분들께 리눅스에서 한글의 완전한 처리를
위한 첫걸음이 승리하기를 빌며 이만 인사에 갈음할까 합니다.
안녕히 계시길...

ddoch 한동훈
Trackback Address :: http://joyholic.kr/trackback/275 관련글 쓰기
Name
Password
Homepage
Secret
2008/03/09 14:13

#include        "ksc.h"

/* KSC5601 -> Unicode 2.0 mapping table, compressed for the 94*94 codeset. */
/* Generated based on  KSC5601.txt at
   ftp://ftp.unicode.org/Public/MAPPINGS/EASTASIA/KSC  */
/*
 * Unlike kuten-table, needed offset is 33 (0x21) instead of
 * 32 for 7-bit portion of each byte.  i.e., a Unicode
 * codepoint for KSC's codepoint (n, m) would be found at
 * index (n-33)*94+m-33.
 */
long tabksc5601[] = {
/* KSC 5601 -> Unicode mapping table; max codepoint = 0x7d7e */
       0x3000,0x3001,0x3002,0x00B7,0x2025,0x2026,0x00A8,
0x3003,0x00AD,0x2015,0x2225,0xFF3C,0x223C,0x2018,0x2019,
0x201C,0x201D,0x3014,0x3015,0x3008,0x3009,0x300A,0x300B,
0x300C,0x300D,0x300E,0x300F,0x3010,0x3011,0x00B1,0x00D7,
0x00F7,0x2260,0x2264,0x2265,0x221E,0x2234,0x00B0,0x2032,
0x2033,0x2103,0x212B,0xFFE0,0xFFE1,0xFFE5,0x2642,0x2640,
0x2220,0x22A5,0x2312,0x2202,0x2207,0x2261,0x2252,0x00A7,
0x203B,0x2606,0x2605,0x25CB,0x25CF,0x25CE,0x25C7,0x25C6,
0x25A1,0x25A0,0x25B3,0x25B2,0x25BD,0x25BC,0x2192,0x2190,
0x2191,0x2193,0x2194,0x3013,0x226A,0x226B,0x221A,0x223D,
0x221D,0x2235,0x222B,0x222C,0x2208,0x220B,0x2286,0x2287,
0x2282,0x2283,0x222A,0x2229,0x2227,0x2228,0xFFE2,
       0x21D2,0x21D4,0x2200,0x2203,0x00B4,0xFF5E,0x02C7,
0x02D8,0x02DD,0x02DA,0x02D9,0x00B8,0x02DB,0x00A1,0x00BF,
0x02D0,0x222E,0x2211,0x220F,0x00A4,0x2109,0x2030,0x25C1,
0x25C0,0x25B7,0x25B6,0x2664,0x2660,0x2661,0x2665,0x2667,
0x2663,0x2299,0x25C8,0x25A3,0x25D0,0x25D1,0x2592,0x25A4,
0x25A5,0x25A8,0x25A7,0x25A6,0x25A9,0x2668,0x260F,0x260E,
0x261C,0x261E,0x00B6,0x2020,0x2021,0x2195,0x2197,0x2199,
0x2196,0x2198,0x266D,0x2669,0x266A,0x266C,0x327F,0x321C,
0x2116,0x33C7,0x2122,0x33C2,0x33D8,0x2121,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       0xFF01,0xFF02,0xFF03,0xFF04,0xFF05,0xFF06,0xFF07,
0xFF08,0xFF09,0xFF0A,0xFF0B,0xFF0C,0xFF0D,0xFF0E,0xFF0F,
0xFF10,0xFF11,0xFF12,0xFF13,0xFF14,0xFF15,0xFF16,0xFF17,
0xFF18,0xFF19,0xFF1A,0xFF1B,0xFF1C,0xFF1D,0xFF1E,0xFF1F,
0xFF20,0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,
0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,
0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,
0xFF38,0xFF39,0xFF3A,0xFF3B,0xFFE6,0xFF3D,0xFF3E,0xFF3F,
0xFF40,0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,
0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,
0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,
0xFF58,0xFF59,0xFF5A,0xFF5B,0xFF5C,0xFF5D,0xFFE3,
       0x3131,0x3132,0x3133,0x3134,0x3135,0x3136,0x3137,
0x3138,0x3139,0x313A,0x313B,0x313C,0x313D,0x313E,0x313F,
0x3140,0x3141,0x3142,0x3143,0x3144,0x3145,0x3146,0x3147,
0x3148,0x3149,0x314A,0x314B,0x314C,0x314D,0x314E,0x314F,
0x3150,0x3151,0x3152,0x3153,0x3154,0x3155,0x3156,0x3157,
0x3158,0x3159,0x315A,0x315B,0x315C,0x315D,0x315E,0x315F,
0x3160,0x3161,0x3162,0x3163,0x3164,0x3165,0x3166,0x3167,
0x3168,0x3169,0x316A,0x316B,0x316C,0x316D,0x316E,0x316F,
0x3170,0x3171,0x3172,0x3173,0x3174,0x3175,0x3176,0x3177,
0x3178,0x3179,0x317A,0x317B,0x317C,0x317D,0x317E,0x317F,
0x3180,0x3181,0x3182,0x3183,0x3184,0x3185,0x3186,0x3187,
0x3188,0x3189,0x318A,0x318B,0x318C,0x318D,0x318E,
       0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,
0x2177,0x2178,0x2179,    -1,    -1,    -1,    -1,    -1,
0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,
0x2168,0x2169,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
0x0398,0x0399,0x039A,0x039B,0x039C,0x039D,0x039E,0x039F,
0x03A0,0x03A1,0x03A3,0x03A4,0x03A5,0x03A6,0x03A7,0x03A8,
0x03A9,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,0x03B1,0x03B2,0x03B3,0x03B4,0x03B5,0x03B6,0x03B7,
0x03B8,0x03B9,0x03BA,0x03BB,0x03BC,0x03BD,0x03BE,0x03BF,
0x03C0,0x03C1,0x03C3,0x03C4,0x03C5,0x03C6,0x03C7,0x03C8,
0x03C9,    -1,    -1,    -1,    -1,    -1,    -1,
       0x2500,0x2502,0x250C,0x2510,0x2518,0x2514,0x251C,
0x252C,0x2524,0x2534,0x253C,0x2501,0x2503,0x250F,0x2513,
0x251B,0x2517,0x2523,0x2533,0x252B,0x253B,0x254B,0x2520,
0x252F,0x2528,0x2537,0x253F,0x251D,0x2530,0x2525,0x2538,
0x2542,0x2512,0x2511,0x251A,0x2519,0x2516,0x2515,0x250E,
0x250D,0x251E,0x251F,0x2521,0x2522,0x2526,0x2527,0x2529,
0x252A,0x252D,0x252E,0x2531,0x2532,0x2535,0x2536,0x2539,
0x253A,0x253D,0x253E,0x2540,0x2541,0x2543,0x2544,0x2545,
0x2546,0x2547,0x2548,0x2549,0x254A,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       0x3395,0x3396,0x3397,0x2113,0x3398,0x33C4,0x33A3,
0x33A4,0x33A5,0x33A6,0x3399,0x339A,0x339B,0x339C,0x339D,
0x339E,0x339F,0x33A0,0x33A1,0x33A2,0x33CA,0x338D,0x338E,
0x338F,0x33CF,0x3388,0x3389,0x33C8,0x33A7,0x33A8,0x33B0,
0x33B1,0x33B2,0x33B3,0x33B4,0x33B5,0x33B6,0x33B7,0x33B8,
0x33B9,0x3380,0x3381,0x3382,0x3383,0x3384,0x33BA,0x33BB,
0x33BC,0x33BD,0x33BE,0x33BF,0x3390,0x3391,0x3392,0x3393,
0x3394,0x2126,0x33C0,0x33C1,0x338A,0x338B,0x338C,0x33D6,
0x33C5,0x33AD,0x33AE,0x33AF,0x33DB,0x33A9,0x33AA,0x33AB,
0x33AC,0x33DD,0x33D0,0x33D3,0x33C3,0x33C9,0x33DC,0x33C6,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       0x00C6,0x00D0,0x00AA,0x0126,    -1,0x0132,    -1,
0x013F,0x0141,0x00D8,0x0152,0x00BA,0x00DE,0x0166,0x014A,
    -1,0x3260,0x3261,0x3262,0x3263,0x3264,0x3265,0x3266,
0x3267,0x3268,0x3269,0x326A,0x326B,0x326C,0x326D,0x326E,
0x326F,0x3270,0x3271,0x3272,0x3273,0x3274,0x3275,0x3276,
0x3277,0x3278,0x3279,0x327A,0x327B,0x24D0,0x24D1,0x24D2,
0x24D3,0x24D4,0x24D5,0x24D6,0x24D7,0x24D8,0x24D9,0x24DA,
0x24DB,0x24DC,0x24DD,0x24DE,0x24DF,0x24E0,0x24E1,0x24E2,
0x24E3,0x24E4,0x24E5,0x24E6,0x24E7,0x24E8,0x24E9,0x2460,
0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,
0x2469,0x246A,0x246B,0x246C,0x246D,0x246E,0x00BD,0x2153,
0x2154,0x00BC,0x00BE,0x215B,0x215C,0x215D,0x215E,
       0x00E6,0x0111,0x00F0,0x0127,0x0131,0x0133,0x0138,
0x0140,0x0142,0x00F8,0x0153,0x00DF,0x00FE,0x0167,0x014B,
0x0149,0x3200,0x3201,0x3202,0x3203,0x3204,0x3205,0x3206,
0x3207,0x3208,0x3209,0x320A,0x320B,0x320C,0x320D,0x320E,
0x320F,0x3210,0x3211,0x3212,0x3213,0x3214,0x3215,0x3216,
0x3217,0x3218,0x3219,0x321A,0x321B,0x249C,0x249D,0x249E,
0x249F,0x24A0,0x24A1,0x24A2,0x24A3,0x24A4,0x24A5,0x24A6,
0x24A7,0x24A8,0x24A9,0x24AA,0x24AB,0x24AC,0x24AD,0x24AE,
0x24AF,0x24B0,0x24B1,0x24B2,0x24B3,0x24B4,0x24B5,0x2474,
0x2475,0x2476,0x2477,0x2478,0x2479,0x247A,0x247B,0x247C,
0x247D,0x247E,0x247F,0x2480,0x2481,0x2482,0x00B9,0x00B2,
0x00B3,0x2074,0x207F,0x2081,0x2082,0x2083,0x2084,
       0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,
0x3048,0x3049,0x304A,0x304B,0x304C,0x304D,0x304E,0x304F,
0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,
0x3058,0x3059,0x305A,0x305B,0x305C,0x305D,0x305E,0x305F,
0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,
0x3068,0x3069,0x306A,0x306B,0x306C,0x306D,0x306E,0x306F,
0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,0x3077,
0x3078,0x3079,0x307A,0x307B,0x307C,0x307D,0x307E,0x307F,
0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,
0x3088,0x3089,0x308A,0x308B,0x308C,0x308D,0x308E,0x308F,
0x3090,0x3091,0x3092,0x3093,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       0x30A1,0x30A2,0x30A3,0x30A4,0x30A5,0x30A6,0x30A7,
0x30A8,0x30A9,0x30AA,0x30AB,0x30AC,0x30AD,0x30AE,0x30AF,
0x30B0,0x30B1,0x30B2,0x30B3,0x30B4,0x30B5,0x30B6,0x30B7,
0x30B8,0x30B9,0x30BA,0x30BB,0x30BC,0x30BD,0x30BE,0x30BF,
0x30C0,0x30C1,0x30C2,0x30C3,0x30C4,0x30C5,0x30C6,0x30C7,
0x30C8,0x30C9,0x30CA,0x30CB,0x30CC,0x30CD,0x30CE,0x30CF,
0x30D0,0x30D1,0x30D2,0x30D3,0x30D4,0x30D5,0x30D6,0x30D7,
0x30D8,0x30D9,0x30DA,0x30DB,0x30DC,0x30DD,0x30DE,0x30DF,
0x30E0,0x30E1,0x30E2,0x30E3,0x30E4,0x30E5,0x30E6,0x30E7,
0x30E8,0x30E9,0x30EA,0x30EB,0x30EC,0x30ED,0x30EE,0x30EF,
0x30F0,0x30F1,0x30F2,0x30F3,0x30F4,0x30F5,0x30F6,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,
0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,
0x041E,0x041F,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,
0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,
0x042E,0x042F,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0451,
0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,
0x043E,0x043F,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,
0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,
0x044E,0x044F,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
           -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
           -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
           -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       0xAC00,0xAC01,0xAC04,0xAC07,0xAC08,0xAC09,0xAC0A,
0xAC10,0xAC11,0xAC12,0xAC13,0xAC14,0xAC15,0xAC16,0xAC17,
0xAC19,0xAC1A,0xAC1B,0xAC1C,0xAC1D,0xAC20,0xAC24,0xAC2C,
0xAC2D,0xAC2F,0xAC30,0xAC31,0xAC38,0xAC39,0xAC3C,0xAC40,
0xAC4B,0xAC4D,0xAC54,0xAC58,0xAC5C,0xAC70,0xAC71,0xAC74,
0xAC77,0xAC78,0xAC7A,0xAC80,0xAC81,0xAC83,0xAC84,0xAC85,
0xAC86,0xAC89,0xAC8A,0xAC8B,0xAC8C,0xAC90,0xAC94,0xAC9C,
0xAC9D,0xAC9F,0xACA0,0xACA1,0xACA8,0xACA9,0xACAA,0xACAC,
0xACAF,0xACB0,0xACB8,0xACB9,0xACBB,0xACBC,0xACBD,0xACC1,
0xACC4,0xACC8,0xACCC,0xACD5,0xACD7,0xACE0,0xACE1,0xACE4,
0xACE7,0xACE8,0xACEA,0xACEC,0xACEF,0xACF0,0xACF1,0xACF3,
0xACF5,0xACF6,0xACFC,0xACFD,0xAD00,0xAD04,0xAD06,
       0xAD0C,0xAD0D,0xAD0F,0xAD11,0xAD18,0xAD1C,0xAD20,
0xAD29,0xAD2C,0xAD2D,0xAD34,0xAD35,0xAD38,0xAD3C,0xAD44,
0xAD45,0xAD47,0xAD49,0xAD50,0xAD54,0xAD58,0xAD61,0xAD63,
0xAD6C,0xAD6D,0xAD70,0xAD73,0xAD74,0xAD75,0xAD76,0xAD7B,
0xAD7C,0xAD7D,0xAD7F,0xAD81,0xAD82,0xAD88,0xAD89,0xAD8C,
0xAD90,0xAD9C,0xAD9D,0xADA4,0xADB7,0xADC0,0xADC1,0xADC4,
0xADC8,0xADD0,0xADD1,0xADD3,0xADDC,0xADE0,0xADE4,0xADF8,
0xADF9,0xADFC,0xADFF,0xAE00,0xAE01,0xAE08,0xAE09,0xAE0B,
0xAE0D,0xAE14,0xAE30,0xAE31,0xAE34,0xAE37,0xAE38,0xAE3A,
0xAE40,0xAE41,0xAE43,0xAE45,0xAE46,0xAE4A,0xAE4C,0xAE4D,
0xAE4E,0xAE50,0xAE54,0xAE56,0xAE5C,0xAE5D,0xAE5F,0xAE60,
0xAE61,0xAE65,0xAE68,0xAE69,0xAE6C,0xAE70,0xAE78,
       0xAE79,0xAE7B,0xAE7C,0xAE7D,0xAE84,0xAE85,0xAE8C,
0xAEBC,0xAEBD,0xAEBE,0xAEC0,0xAEC4,0xAECC,0xAECD,0xAECF,
0xAED0,0xAED1,0xAED8,0xAED9,0xAEDC,0xAEE8,0xAEEB,0xAEED,
0xAEF4,0xAEF8,0xAEFC,0xAF07,0xAF08,0xAF0D,0xAF10,0xAF2C,
0xAF2D,0xAF30,0xAF32,0xAF34,0xAF3C,0xAF3D,0xAF3F,0xAF41,
0xAF42,0xAF43,0xAF48,0xAF49,0xAF50,0xAF5C,0xAF5D,0xAF64,
0xAF65,0xAF79,0xAF80,0xAF84,0xAF88,0xAF90,0xAF91,0xAF95,
0xAF9C,0xAFB8,0xAFB9,0xAFBC,0xAFC0,0xAFC7,0xAFC8,0xAFC9,
0xAFCB,0xAFCD,0xAFCE,0xAFD4,0xAFDC,0xAFE8,0xAFE9,0xAFF0,
0xAFF1,0xAFF4,0xAFF8,0xB000,0xB001,0xB004,0xB00C,0xB010,
0xB014,0xB01C,0xB01D,0xB028,0xB044,0xB045,0xB048,0xB04A,
0xB04C,0xB04E,0xB053,0xB054,0xB055,0xB057,0xB059,
       0xB05D,0xB07C,0xB07D,0xB080,0xB084,0xB08C,0xB08D,
0xB08F,0xB091,0xB098,0xB099,0xB09A,0xB09C,0xB09F,0xB0A0,
0xB0A1,0xB0A2,0xB0A8,0xB0A9,0xB0AB,0xB0AC,0xB0AD,0xB0AE,
0xB0AF,0xB0B1,0xB0B3,0xB0B4,0xB0B5,0xB0B8,0xB0BC,0xB0C4,
0xB0C5,0xB0C7,0xB0C8,0xB0C9,0xB0D0,0xB0D1,0xB0D4,0xB0D8,
0xB0E0,0xB0E5,0xB108,0xB109,0xB10B,0xB10C,0xB110,0xB112,
0xB113,0xB118,0xB119,0xB11B,0xB11C,0xB11D,0xB123,0xB124,
0xB125,0xB128,0xB12C,0xB134,0xB135,0xB137,0xB138,0xB139,
0xB140,0xB141,0xB144,0xB148,0xB150,0xB151,0xB154,0xB155,
0xB158,0xB15C,0xB160,0xB178,0xB179,0xB17C,0xB180,0xB182,
0xB188,0xB189,0xB18B,0xB18D,0xB192,0xB193,0xB194,0xB198,
0xB19C,0xB1A8,0xB1CC,0xB1D0,0xB1D4,0xB1DC,0xB1DD,
       0xB1DF,0xB1E8,0xB1E9,0xB1EC,0xB1F0,0xB1F9,0xB1FB,
0xB1FD,0xB204,0xB205,0xB208,0xB20B,0xB20C,0xB214,0xB215,
0xB217,0xB219,0xB220,0xB234,0xB23C,0xB258,0xB25C,0xB260,
0xB268,0xB269,0xB274,0xB275,0xB27C,0xB284,0xB285,0xB289,
0xB290,0xB291,0xB294,0xB298,0xB299,0xB29A,0xB2A0,0xB2A1,
0xB2A3,0xB2A5,0xB2A6,0xB2AA,0xB2AC,0xB2B0,0xB2B4,0xB2C8,
0xB2C9,0xB2CC,0xB2D0,0xB2D2,0xB2D8,0xB2D9,0xB2DB,0xB2DD,
0xB2E2,0xB2E4,0xB2E5,0xB2E6,0xB2E8,0xB2EB,0xB2EC,0xB2ED,
0xB2EE,0xB2EF,0xB2F3,0xB2F4,0xB2F5,0xB2F7,0xB2F8,0xB2F9,
0xB2FA,0xB2FB,0xB2FF,0xB300,0xB301,0xB304,0xB308,0xB310,
0xB311,0xB313,0xB314,0xB315,0xB31C,0xB354,0xB355,0xB356,
0xB358,0xB35B,0xB35C,0xB35E,0xB35F,0xB364,0xB365,
       0xB367,0xB369,0xB36B,0xB36E,0xB370,0xB371,0xB374,
0xB378,0xB380,0xB381,0xB383,0xB384,0xB385,0xB38C,0xB390,
0xB394,0xB3A0,0xB3A1,0xB3A8,0xB3AC,0xB3C4,0xB3C5,0xB3C8,
0xB3CB,0xB3CC,0xB3CE,0xB3D0,0xB3D4,0xB3D5,0xB3D7,0xB3D9,
0xB3DB,0xB3DD,0xB3E0,0xB3E4,0xB3E8,0xB3FC,0xB410,0xB418,
0xB41C,0xB420,0xB428,0xB429,0xB42B,0xB434,0xB450,0xB451,
0xB454,0xB458,0xB460,0xB461,0xB463,0xB465,0xB46C,0xB480,
0xB488,0xB49D,0xB4A4,0xB4A8,0xB4AC,0xB4B5,0xB4B7,0xB4B9,
0xB4C0,0xB4C4,0xB4C8,0xB4D0,0xB4D5,0xB4DC,0xB4DD,0xB4E0,
0xB4E3,0xB4E4,0xB4E6,0xB4EC,0xB4ED,0xB4EF,0xB4F1,0xB4F8,
0xB514,0xB515,0xB518,0xB51B,0xB51C,0xB524,0xB525,0xB527,
0xB528,0xB529,0xB52A,0xB530,0xB531,0xB534,0xB538,
       0xB540,0xB541,0xB543,0xB544,0xB545,0xB54B,0xB54C,
0xB54D,0xB550,0xB554,0xB55C,0xB55D,0xB55F,0xB560,0xB561,
0xB5A0,0xB5A1,0xB5A4,0xB5A8,0xB5AA,0xB5AB,0xB5B0,0xB5B1,
0xB5B3,0xB5B4,0xB5B5,0xB5BB,0xB5BC,0xB5BD,0xB5C0,0xB5C4,
0xB5CC,0xB5CD,0xB5CF,0xB5D0,0xB5D1,0xB5D8,0xB5EC,0xB610,
0xB611,0xB614,0xB618,0xB625,0xB62C,0xB634,0xB648,0xB664,
0xB668,0xB69C,0xB69D,0xB6A0,0xB6A4,0xB6AB,0xB6AC,0xB6B1,
0xB6D4,0xB6F0,0xB6F4,0xB6F8,0xB700,0xB701,0xB705,0xB728,
0xB729,0xB72C,0xB72F,0xB730,0xB738,0xB739,0xB73B,0xB744,
0xB748,0xB74C,0xB754,0xB755,0xB760,0xB764,0xB768,0xB770,
0xB771,0xB773,0xB775,0xB77C,0xB77D,0xB780,0xB784,0xB78C,
0xB78D,0xB78F,0xB790,0xB791,0xB792,0xB796,0xB797,
       0xB798,0xB799,0xB79C,0xB7A0,0xB7A8,0xB7A9,0xB7AB,
0xB7AC,0xB7AD,0xB7B4,0xB7B5,0xB7B8,0xB7C7,0xB7C9,0xB7EC,
0xB7ED,0xB7F0,0xB7F4,0xB7FC,0xB7FD,0xB7FF,0xB800,0xB801,
0xB807,0xB808,0xB809,0xB80C,0xB810,0xB818,0xB819,0xB81B,
0xB81D,0xB824,0xB825,0xB828,0xB82C,0xB834,0xB835,0xB837,
0xB838,0xB839,0xB840,0xB844,0xB851,0xB853,0xB85C,0xB85D,
0xB860,0xB864,0xB86C,0xB86D,0xB86F,0xB871,0xB878,0xB87C,
0xB88D,0xB8A8,0xB8B0,0xB8B4,0xB8B8,0xB8C0,0xB8C1,0xB8C3,
0xB8C5,0xB8CC,0xB8D0,0xB8D4,0xB8DD,0xB8DF,0xB8E1,0xB8E8,
0xB8E9,0xB8EC,0xB8F0,0xB8F8,0xB8F9,0xB8FB,0xB8FD,0xB904,
0xB918,0xB920,0xB93C,0xB93D,0xB940,0xB944,0xB94C,0xB94F,
0xB951,0xB958,0xB959,0xB95C,0xB960,0xB968,0xB969,
       0xB96B,0xB96D,0xB974,0xB975,0xB978,0xB97C,0xB984,
0xB985,0xB987,0xB989,0xB98A,0xB98D,0xB98E,0xB9AC,0xB9AD,
0xB9B0,0xB9B4,0xB9BC,0xB9BD,0xB9BF,0xB9C1,0xB9C8,0xB9C9,
0xB9CC,0xB9CE,0xB9CF,0xB9D0,0xB9D1,0xB9D2,0xB9D8,0xB9D9,
0xB9DB,0xB9DD,0xB9DE,0xB9E1,0xB9E3,0xB9E4,0xB9E5,0xB9E8,
0xB9EC,0xB9F4,0xB9F5,0xB9F7,0xB9F8,0xB9F9,0xB9FA,0xBA00,
0xBA01,0xBA08,0xBA15,0xBA38,0xBA39,0xBA3C,0xBA40,0xBA42,
0xBA48,0xBA49,0xBA4B,0xBA4D,0xBA4E,0xBA53,0xBA54,0xBA55,
0xBA58,0xBA5C,0xBA64,0xBA65,0xBA67,0xBA68,0xBA69,0xBA70,
0xBA71,0xBA74,0xBA78,0xBA83,0xBA84,0xBA85,0xBA87,0xBA8C,
0xBAA8,0xBAA9,0xBAAB,0xBAAC,0xBAB0,0xBAB2,0xBAB8,0xBAB9,
0xBABB,0xBABD,0xBAC4,0xBAC8,0xBAD8,0xBAD9,0xBAFC,
       0xBB00,0xBB04,0xBB0D,0xBB0F,0xBB11,0xBB18,0xBB1C,
0xBB20,0xBB29,0xBB2B,0xBB34,0xBB35,0xBB36,0xBB38,0xBB3B,
0xBB3C,0xBB3D,0xBB3E,0xBB44,0xBB45,0xBB47,0xBB49,0xBB4D,
0xBB4F,0xBB50,0xBB54,0xBB58,0xBB61,0xBB63,0xBB6C,0xBB88,
0xBB8C,0xBB90,0xBBA4,0xBBA8,0xBBAC,0xBBB4,0xBBB7,0xBBC0,
0xBBC4,0xBBC8,0xBBD0,0xBBD3,0xBBF8,0xBBF9,0xBBFC,0xBBFF,
0xBC00,0xBC02,0xBC08,0xBC09,0xBC0B,0xBC0C,0xBC0D,0xBC0F,
0xBC11,0xBC14,0xBC15,0xBC16,0xBC17,0xBC18,0xBC1B,0xBC1C,
0xBC1D,0xBC1E,0xBC1F,0xBC24,0xBC25,0xBC27,0xBC29,0xBC2D,
0xBC30,0xBC31,0xBC34,0xBC38,0xBC40,0xBC41,0xBC43,0xBC44,
0xBC45,0xBC49,0xBC4C,0xBC4D,0xBC50,0xBC5D,0xBC84,0xBC85,
0xBC88,0xBC8B,0xBC8C,0xBC8E,0xBC94,0xBC95,0xBC97,
       0xBC99,0xBC9A,0xBCA0,0xBCA1,0xBCA4,0xBCA7,0xBCA8,
0xBCB0,0xBCB1,0xBCB3,0xBCB4,0xBCB5,0xBCBC,0xBCBD,0xBCC0,
0xBCC4,0xBCCD,0xBCCF,0xBCD0,0xBCD1,0xBCD5,0xBCD8,0xBCDC,
0xBCF4,0xBCF5,0xBCF6,0xBCF8,0xBCFC,0xBD04,0xBD05,0xBD07,
0xBD09,0xBD10,0xBD14,0xBD24,0xBD2C,0xBD40,0xBD48,0xBD49,
0xBD4C,0xBD50,0xBD58,0xBD59,0xBD64,0xBD68,0xBD80,0xBD81,
0xBD84,0xBD87,0xBD88,0xBD89,0xBD8A,0xBD90,0xBD91,0xBD93,
0xBD95,0xBD99,0xBD9A,0xBD9C,0xBDA4,0xBDB0,0xBDB8,0xBDD4,
0xBDD5,0xBDD8,0xBDDC,0xBDE9,0xBDF0,0xBDF4,0xBDF8,0xBE00,
0xBE03,0xBE05,0xBE0C,0xBE0D,0xBE10,0xBE14,0xBE1C,0xBE1D,
0xBE1F,0xBE44,0xBE45,0xBE48,0xBE4C,0xBE4E,0xBE54,0xBE55,
0xBE57,0xBE59,0xBE5A,0xBE5B,0xBE60,0xBE61,0xBE64,
       0xBE68,0xBE6A,0xBE70,0xBE71,0xBE73,0xBE74,0xBE75,
0xBE7B,0xBE7C,0xBE7D,0xBE80,0xBE84,0xBE8C,0xBE8D,0xBE8F,
0xBE90,0xBE91,0xBE98,0xBE99,0xBEA8,0xBED0,0xBED1,0xBED4,
0xBED7,0xBED8,0xBEE0,0xBEE3,0xBEE4,0xBEE5,0xBEEC,0xBF01,
0xBF08,0xBF09,0xBF18,0xBF19,0xBF1B,0xBF1C,0xBF1D,0xBF40,
0xBF41,0xBF44,0xBF48,0xBF50,0xBF51,0xBF55,0xBF94,0xBFB0,
0xBFC5,0xBFCC,0xBFCD,0xBFD0,0xBFD4,0xBFDC,0xBFDF,0xBFE1,
0xC03C,0xC051,0xC058,0xC05C,0xC060,0xC068,0xC069,0xC090,
0xC091,0xC094,0xC098,0xC0A0,0xC0A1,0xC0A3,0xC0A5,0xC0AC,
0xC0AD,0xC0AF,0xC0B0,0xC0B3,0xC0B4,0xC0B5,0xC0B6,0xC0BC,
0xC0BD,0xC0BF,0xC0C0,0xC0C1,0xC0C5,0xC0C8,0xC0C9,0xC0CC,
0xC0D0,0xC0D8,0xC0D9,0xC0DB,0xC0DC,0xC0DD,0xC0E4,
       0xC0E5,0xC0E8,0xC0EC,0xC0F4,0xC0F5,0xC0F7,0xC0F9,
0xC100,0xC104,0xC108,0xC110,0xC115,0xC11C,0xC11D,0xC11E,
0xC11F,0xC120,0xC123,0xC124,0xC126,0xC127,0xC12C,0xC12D,
0xC12F,0xC130,0xC131,0xC136,0xC138,0xC139,0xC13C,0xC140,
0xC148,0xC149,0xC14B,0xC14C,0xC14D,0xC154,0xC155,0xC158,
0xC15C,0xC164,0xC165,0xC167,0xC168,0xC169,0xC170,0xC174,
0xC178,0xC185,0xC18C,0xC18D,0xC18E,0xC190,0xC194,0xC196,
0xC19C,0xC19D,0xC19F,0xC1A1,0xC1A5,0xC1A8,0xC1A9,0xC1AC,
0xC1B0,0xC1BD,0xC1C4,0xC1C8,0xC1CC,0xC1D4,0xC1D7,0xC1D8,
0xC1E0,0xC1E4,0xC1E8,0xC1F0,0xC1F1,0xC1F3,0xC1FC,0xC1FD,
0xC200,0xC204,0xC20C,0xC20D,0xC20F,0xC211,0xC218,0xC219,
0xC21C,0xC21F,0xC220,0xC228,0xC229,0xC22B,0xC22D,
       0xC22F,0xC231,0xC232,0xC234,0xC248,0xC250,0xC251,
0xC254,0xC258,0xC260,0xC265,0xC26C,0xC26D,0xC270,0xC274,
0xC27C,0xC27D,0xC27F,0xC281,0xC288,0xC289,0xC290,0xC298,
0xC29B,0xC29D,0xC2A4,0xC2A5,0xC2A8,0xC2AC,0xC2AD,0xC2B4,
0xC2B5,0xC2B7,0xC2B9,0xC2DC,0xC2DD,0xC2E0,0xC2E3,0xC2E4,
0xC2EB,0xC2EC,0xC2ED,0xC2EF,0xC2F1,0xC2F6,0xC2F8,0xC2F9,
0xC2FB,0xC2FC,0xC300,0xC308,0xC309,0xC30C,0xC30D,0xC313,
0xC314,0xC315,0xC318,0xC31C,0xC324,0xC325,0xC328,0xC329,
0xC345,0xC368,0xC369,0xC36C,0xC370,0xC372,0xC378,0xC379,
0xC37C,0xC37D,0xC384,0xC388,0xC38C,0xC3C0,0xC3D8,0xC3D9,
0xC3DC,0xC3DF,0xC3E0,0xC3E2,0xC3E8,0xC3E9,0xC3ED,0xC3F4,
0xC3F5,0xC3F8,0xC408,0xC410,0xC424,0xC42C,0xC430,
       0xC434,0xC43C,0xC43D,0xC448,0xC464,0xC465,0xC468,
0xC46C,0xC474,0xC475,0xC479,0xC480,0xC494,0xC49C,0xC4B8,
0xC4BC,0xC4E9,0xC4F0,0xC4F1,0xC4F4,0xC4F8,0xC4FA,0xC4FF,
0xC500,0xC501,0xC50C,0xC510,0xC514,0xC51C,0xC528,0xC529,
0xC52C,0xC530,0xC538,0xC539,0xC53B,0xC53D,0xC544,0xC545,
0xC548,0xC549,0xC54A,0xC54C,0xC54D,0xC54E,0xC553,0xC554,
0xC555,0xC557,0xC558,0xC559,0xC55D,0xC55E,0xC560,0xC561,
0xC564,0xC568,0xC570,0xC571,0xC573,0xC574,0xC575,0xC57C,
0xC57D,0xC580,0xC584,0xC587,0xC58C,0xC58D,0xC58F,0xC591,
0xC595,0xC597,0xC598,0xC59C,0xC5A0,0xC5A9,0xC5B4,0xC5B5,
0xC5B8,0xC5B9,0xC5BB,0xC5BC,0xC5BD,0xC5BE,0xC5C4,0xC5C5,
0xC5C6,0xC5C7,0xC5C8,0xC5C9,0xC5CA,0xC5CC,0xC5CE,
       0xC5D0,0xC5D1,0xC5D4,0xC5D8,0xC5E0,0xC5E1,0xC5E3,
0xC5E5,0xC5EC,0xC5ED,0xC5EE,0xC5F0,0xC5F4,0xC5F6,0xC5F7,
0xC5FC,0xC5FD,0xC5FE,0xC5FF,0xC600,0xC601,0xC605,0xC606,
0xC607,0xC608,0xC60C,0xC610,0xC618,0xC619,0xC61B,0xC61C,
0xC624,0xC625,0xC628,0xC62C,0xC62D,0xC62E,0xC630,0xC633,
0xC634,0xC635,0xC637,0xC639,0xC63B,0xC640,0xC641,0xC644,
0xC648,0xC650,0xC651,0xC653,0xC654,0xC655,0xC65C,0xC65D,
0xC660,0xC66C,0xC66F,0xC671,0xC678,0xC679,0xC67C,0xC680,
0xC688,0xC689,0xC68B,0xC68D,0xC694,0xC695,0xC698,0xC69C,
0xC6A4,0xC6A5,0xC6A7,0xC6A9,0xC6B0,0xC6B1,0xC6B4,0xC6B8,
0xC6B9,0xC6BA,0xC6C0,0xC6C1,0xC6C3,0xC6C5,0xC6CC,0xC6CD,
0xC6D0,0xC6D4,0xC6DC,0xC6DD,0xC6E0,0xC6E1,0xC6E8,
       0xC6E9,0xC6EC,0xC6F0,0xC6F8,0xC6F9,0xC6FD,0xC704,
0xC705,0xC708,0xC70C,0xC714,0xC715,0xC717,0xC719,0xC720,
0xC721,0xC724,0xC728,0xC730,0xC731,0xC733,0xC735,0xC737,
0xC73C,0xC73D,0xC740,0xC744,0xC74A,0xC74C,0xC74D,0xC74F,
0xC751,0xC752,0xC753,0xC754,0xC755,0xC756,0xC757,0xC758,
0xC75C,0xC760,0xC768,0xC76B,0xC774,0xC775,0xC778,0xC77C,
0xC77D,0xC77E,0xC783,0xC784,0xC785,0xC787,0xC788,0xC789,
0xC78A,0xC78E,0xC790,0xC791,0xC794,0xC796,0xC797,0xC798,
0xC79A,0xC7A0,0xC7A1,0xC7A3,0xC7A4,0xC7A5,0xC7A6,0xC7AC,
0xC7AD,0xC7B0,0xC7B4,0xC7BC,0xC7BD,0xC7BF,0xC7C0,0xC7C1,
0xC7C8,0xC7C9,0xC7CC,0xC7CE,0xC7D0,0xC7D8,0xC7DD,0xC7E4,
0xC7E8,0xC7EC,0xC800,0xC801,0xC804,0xC808,0xC80A,
       0xC810,0xC811,0xC813,0xC815,0xC816,0xC81C,0xC81D,
0xC820,0xC824,0xC82C,0xC82D,0xC82F,0xC831,0xC838,0xC83C,
0xC840,0xC848,0xC849,0xC84C,0xC84D,0xC854,0xC870,0xC871,
0xC874,0xC878,0xC87A,0xC880,0xC881,0xC883,0xC885,0xC886,
0xC887,0xC88B,0xC88C,0xC88D,0xC894,0xC89D,0xC89F,0xC8A1,
0xC8A8,0xC8BC,0xC8BD,0xC8C4,0xC8C8,0xC8CC,0xC8D4,0xC8D5,
0xC8D7,0xC8D9,0xC8E0,0xC8E1,0xC8E4,0xC8F5,0xC8FC,0xC8FD,
0xC900,0xC904,0xC905,0xC906,0xC90C,0xC90D,0xC90F,0xC911,
0xC918,0xC92C,0xC934,0xC950,0xC951,0xC954,0xC958,0xC960,
0xC961,0xC963,0xC96C,0xC970,0xC974,0xC97C,0xC988,0xC989,
0xC98C,0xC990,0xC998,0xC999,0xC99B,0xC99D,0xC9C0,0xC9C1,
0xC9C4,0xC9C7,0xC9C8,0xC9CA,0xC9D0,0xC9D1,0xC9D3,
       0xC9D5,0xC9D6,0xC9D9,0xC9DA,0xC9DC,0xC9DD,0xC9E0,
0xC9E2,0xC9E4,0xC9E7,0xC9EC,0xC9ED,0xC9EF,0xC9F0,0xC9F1,
0xC9F8,0xC9F9,0xC9FC,0xCA00,0xCA08,0xCA09,0xCA0B,0xCA0C,
0xCA0D,0xCA14,0xCA18,0xCA29,0xCA4C,0xCA4D,0xCA50,0xCA54,
0xCA5C,0xCA5D,0xCA5F,0xCA60,0xCA61,0xCA68,0xCA7D,0xCA84,
0xCA98,0xCABC,0xCABD,0xCAC0,0xCAC4,0xCACC,0xCACD,0xCACF,
0xCAD1,0xCAD3,0xCAD8,0xCAD9,0xCAE0,0xCAEC,0xCAF4,0xCB08,
0xCB10,0xCB14,0xCB18,0xCB20,0xCB21,0xCB41,0xCB48,0xCB49,
0xCB4C,0xCB50,0xCB58,0xCB59,0xCB5D,0xCB64,0xCB78,0xCB79,
0xCB9C,0xCBB8,0xCBD4,0xCBE4,0xCBE7,0xCBE9,0xCC0C,0xCC0D,
0xCC10,0xCC14,0xCC1C,0xCC1D,0xCC21,0xCC22,0xCC27,0xCC28,
0xCC29,0xCC2C,0xCC2E,0xCC30,0xCC38,0xCC39,0xCC3B,
       0xCC3C,0xCC3D,0xCC3E,0xCC44,0xCC45,0xCC48,0xCC4C,
0xCC54,0xCC55,0xCC57,0xCC58,0xCC59,0xCC60,0xCC64,0xCC66,
0xCC68,0xCC70,0xCC75,0xCC98,0xCC99,0xCC9C,0xCCA0,0xCCA8,
0xCCA9,0xCCAB,0xCCAC,0xCCAD,0xCCB4,0xCCB5,0xCCB8,0xCCBC,
0xCCC4,0xCCC5,0xCCC7,0xCCC9,0xCCD0,0xCCD4,0xCCE4,0xCCEC,
0xCCF0,0xCD01,0xCD08,0xCD09,0xCD0C,0xCD10,0xCD18,0xCD19,
0xCD1B,0xCD1D,0xCD24,0xCD28,0xCD2C,0xCD39,0xCD5C,0xCD60,
0xCD64,0xCD6C,0xCD6D,0xCD6F,0xCD71,0xCD78,0xCD88,0xCD94,
0xCD95,0xCD98,0xCD9C,0xCDA4,0xCDA5,0xCDA7,0xCDA9,0xCDB0,
0xCDC4,0xCDCC,0xCDD0,0xCDE8,0xCDEC,0xCDF0,0xCDF8,0xCDF9,
0xCDFB,0xCDFD,0xCE04,0xCE08,0xCE0C,0xCE14,0xCE19,0xCE20,
0xCE21,0xCE24,0xCE28,0xCE30,0xCE31,0xCE33,0xCE35,
       0xCE58,0xCE59,0xCE5C,0xCE5F,0xCE60,0xCE61,0xCE68,
0xCE69,0xCE6B,0xCE6D,0xCE74,0xCE75,0xCE78,0xCE7C,0xCE84,
0xCE85,0xCE87,0xCE89,0xCE90,0xCE91,0xCE94,0xCE98,0xCEA0,
0xCEA1,0xCEA3,0xCEA4,0xCEA5,0xCEAC,0xCEAD,0xCEC1,0xCEE4,
0xCEE5,0xCEE8,0xCEEB,0xCEEC,0xCEF4,0xCEF5,0xCEF7,0xCEF8,
0xCEF9,0xCF00,0xCF01,0xCF04,0xCF08,0xCF10,0xCF11,0xCF13,
0xCF15,0xCF1C,0xCF20,0xCF24,0xCF2C,0xCF2D,0xCF2F,0xCF30,
0xCF31,0xCF38,0xCF54,0xCF55,0xCF58,0xCF5C,0xCF64,0xCF65,
0xCF67,0xCF69,0xCF70,0xCF71,0xCF74,0xCF78,0xCF80,0xCF85,
0xCF8C,0xCFA1,0xCFA8,0xCFB0,0xCFC4,0xCFE0,0xCFE1,0xCFE4,
0xCFE8,0xCFF0,0xCFF1,0xCFF3,0xCFF5,0xCFFC,0xD000,0xD004,
0xD011,0xD018,0xD02D,0xD034,0xD035,0xD038,0xD03C,
       0xD044,0xD045,0xD047,0xD049,0xD050,0xD054,0xD058,
0xD060,0xD06C,0xD06D,0xD070,0xD074,0xD07C,0xD07D,0xD081,
0xD0A4,0xD0A5,0xD0A8,0xD0AC,0xD0B4,0xD0B5,0xD0B7,0xD0B9,
0xD0C0,0xD0C1,0xD0C4,0xD0C8,0xD0C9,0xD0D0,0xD0D1,0xD0D3,
0xD0D4,0xD0D5,0xD0DC,0xD0DD,0xD0E0,0xD0E4,0xD0EC,0xD0ED,
0xD0EF,0xD0F0,0xD0F1,0xD0F8,0xD10D,0xD130,0xD131,0xD134,
0xD138,0xD13A,0xD140,0xD141,0xD143,0xD144,0xD145,0xD14C,
0xD14D,0xD150,0xD154,0xD15C,0xD15D,0xD15F,0xD161,0xD168,
0xD16C,0xD17C,0xD184,0xD188,0xD1A0,0xD1A1,0xD1A4,0xD1A8,
0xD1B0,0xD1B1,0xD1B3,0xD1B5,0xD1BA,0xD1BC,0xD1C0,0xD1D8,
0xD1F4,0xD1F8,0xD207,0xD209,0xD210,0xD22C,0xD22D,0xD230,
0xD234,0xD23C,0xD23D,0xD23F,0xD241,0xD248,0xD25C,
       0xD264,0xD280,0xD281,0xD284,0xD288,0xD290,0xD291,
0xD295,0xD29C,0xD2A0,0xD2A4,0xD2AC,0xD2B1,0xD2B8,0xD2B9,
0xD2BC,0xD2BF,0xD2C0,0xD2C2,0xD2C8,0xD2C9,0xD2CB,0xD2D4,
0xD2D8,0xD2DC,0xD2E4,0xD2E5,0xD2F0,0xD2F1,0xD2F4,0xD2F8,
0xD300,0xD301,0xD303,0xD305,0xD30C,0xD30D,0xD30E,0xD310,
0xD314,0xD316,0xD31C,0xD31D,0xD31F,0xD320,0xD321,0xD325,
0xD328,0xD329,0xD32C,0xD330,0xD338,0xD339,0xD33B,0xD33C,
0xD33D,0xD344,0xD345,0xD37C,0xD37D,0xD380,0xD384,0xD38C,
0xD38D,0xD38F,0xD390,0xD391,0xD398,0xD399,0xD39C,0xD3A0,
0xD3A8,0xD3A9,0xD3AB,0xD3AD,0xD3B4,0xD3B8,0xD3BC,0xD3C4,
0xD3C5,0xD3C8,0xD3C9,0xD3D0,0xD3D8,0xD3E1,0xD3E3,0xD3EC,
0xD3ED,0xD3F0,0xD3F4,0xD3FC,0xD3FD,0xD3FF,0xD401,
       0xD408,0xD41D,0xD440,0xD444,0xD45C,0xD460,0xD464,
0xD46D,0xD46F,0xD478,0xD479,0xD47C,0xD47F,0xD480,0xD482,
0xD488,0xD489,0xD48B,0xD48D,0xD494,0xD4A9,0xD4CC,0xD4D0,
0xD4D4,0xD4DC,0xD4DF,0xD4E8,0xD4EC,0xD4F0,0xD4F8,0xD4FB,
0xD4FD,0xD504,0xD508,0xD50C,0xD514,0xD515,0xD517,0xD53C,
0xD53D,0xD540,0xD544,0xD54C,0xD54D,0xD54F,0xD551,0xD558,
0xD559,0xD55C,0xD560,0xD565,0xD568,0xD569,0xD56B,0xD56D,
0xD574,0xD575,0xD578,0xD57C,0xD584,0xD585,0xD587,0xD588,
0xD589,0xD590,0xD5A5,0xD5C8,0xD5C9,0xD5CC,0xD5D0,0xD5D2,
0xD5D8,0xD5D9,0xD5DB,0xD5DD,0xD5E4,0xD5E5,0xD5E8,0xD5EC,
0xD5F4,0xD5F5,0xD5F7,0xD5F9,0xD600,0xD601,0xD604,0xD608,
0xD610,0xD611,0xD613,0xD614,0xD615,0xD61C,0xD620,
       0xD624,0xD62D,0xD638,0xD639,0xD63C,0xD640,0xD645,
0xD648,0xD649,0xD64B,0xD64D,0xD651,0xD654,0xD655,0xD658,
0xD65C,0xD667,0xD669,0xD670,0xD671,0xD674,0xD683,0xD685,
0xD68C,0xD68D,0xD690,0xD694,0xD69D,0xD69F,0xD6A1,0xD6A8,
0xD6AC,0xD6B0,0xD6B9,0xD6BB,0xD6C4,0xD6C5,0xD6C8,0xD6CC,
0xD6D1,0xD6D4,0xD6D7,0xD6D9,0xD6E0,0xD6E4,0xD6E8,0xD6F0,
0xD6F5,0xD6FC,0xD6FD,0xD700,0xD704,0xD711,0xD718,0xD719,
0xD71C,0xD720,0xD728,0xD729,0xD72B,0xD72D,0xD734,0xD735,
0xD738,0xD73C,0xD744,0xD747,0xD749,0xD750,0xD751,0xD754,
0xD756,0xD757,0xD758,0xD759,0xD760,0xD761,0xD763,0xD765,
0xD769,0xD76C,0xD770,0xD774,0xD77C,0xD77D,0xD781,0xD788,
0xD789,0xD78C,0xD790,0xD798,0xD799,0xD79B,0xD79D,
           -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       0x4F3D,0x4F73,0x5047,0x50F9,0x52A0,0x53EF,0x5475,
0x54E5,0x5609,0x5AC1,0x5BB6,0x6687,0x67B6,0x67B7,0x67EF,
0x6B4C,0x73C2,0x75C2,0x7A3C,0x82DB,0x8304,0x8857,0x8888,
0x8A36,0x8CC8,0x8DCF,0x8EFB,0x8FE6,0x99D5,0x523B,0x5374,
0x5404,0x606A,0x6164,0x6BBC,0x73CF,0x811A,0x89BA,0x89D2,
0x95A3,0x4F83,0x520A,0x58BE,0x5978,0x59E6,0x5E72,0x5E79,
0x61C7,0x63C0,0x6746,0x67EC,0x687F,0x6F97,0x764E,0x770B,
0x78F5,0x7A08,0x7AFF,0x7C21,0x809D,0x826E,0x8271,0x8AEB,
0x9593,0x4E6B,0x559D,0x66F7,0x6E34,0x78A3,0x7AED,0x845B,
0x8910,0x874E,0x97A8,0x52D8,0x574E,0x582A,0x5D4C,0x611F,
0x61BE,0x6221,0x6562,0x67D1,0x6A44,0x6E1B,0x7518,0x75B3,
0x76E3,0x77B0,0x7D3A,0x90AF,0x9451,0x9452,0x9F95,
       0x5323,0x5CAC,0x7532,0x80DB,0x9240,0x9598,0x525B,
0x5808,0x59DC,0x5CA1,0x5D17,0x5EB7,0x5F3A,0x5F4A,0x6177,
0x6C5F,0x757A,0x7586,0x7CE0,0x7D73,0x7DB1,0x7F8C,0x8154,
0x8221,0x8591,0x8941,0x8B1B,0x92FC,0x964D,0x9C47,0x4ECB,
0x4EF7,0x500B,0x51F1,0x584F,0x6137,0x613E,0x6168,0x6539,
0x69EA,0x6F11,0x75A5,0x7686,0x76D6,0x7B87,0x82A5,0x84CB,
0xF900,0x93A7,0x958B,0x5580,0x5BA2,0x5751,0xF901,0x7CB3,
0x7FB9,0x91B5,0x5028,0x53BB,0x5C45,0x5DE8,0x62D2,0x636E,
0x64DA,0x64E7,0x6E20,0x70AC,0x795B,0x8DDD,0x8E1E,0xF902,
0x907D,0x9245,0x92F8,0x4E7E,0x4EF6,0x5065,0x5DFE,0x5EFA,
0x6106,0x6957,0x8171,0x8654,0x8E47,0x9375,0x9A2B,0x4E5E,
0x5091,0x6770,0x6840,0x5109,0x528D,0x5292,0x6AA2,
       0x77BC,0x9210,0x9ED4,0x52AB,0x602F,0x8FF2,0x5048,
0x61A9,0x63ED,0x64CA,0x683C,0x6A84,0x6FC0,0x8188,0x89A1,
0x9694,0x5805,0x727D,0x72AC,0x7504,0x7D79,0x7E6D,0x80A9,
0x898B,0x8B74,0x9063,0x9D51,0x6289,0x6C7A,0x6F54,0x7D50,
0x7F3A,0x8A23,0x517C,0x614A,0x7B9D,0x8B19,0x9257,0x938C,
0x4EAC,0x4FD3,0x501E,0x50BE,0x5106,0x52C1,0x52CD,0x537F,
0x5770,0x5883,0x5E9A,0x5F91,0x6176,0x61AC,0x64CE,0x656C,
0x666F,0x66BB,0x66F4,0x6897,0x6D87,0x7085,0x70F1,0x749F,
0x74A5,0x74CA,0x75D9,0x786C,0x78EC,0x7ADF,0x7AF6,0x7D45,
0x7D93,0x8015,0x803F,0x811B,0x8396,0x8B66,0x8F15,0x9015,
0x93E1,0x9803,0x9838,0x9A5A,0x9BE8,0x4FC2,0x5553,0x583A,
0x5951,0x5B63,0x5C46,0x60B8,0x6212,0x6842,0x68B0,
       0x68E8,0x6EAA,0x754C,0x7678,0x78CE,0x7A3D,0x7CFB,
0x7E6B,0x7E7C,0x8A08,0x8AA1,0x8C3F,0x968E,0x9DC4,0x53E4,
0x53E9,0x544A,0x5471,0x56FA,0x59D1,0x5B64,0x5C3B,0x5EAB,
0x62F7,0x6537,0x6545,0x6572,0x66A0,0x67AF,0x69C1,0x6CBD,
0x75FC,0x7690,0x777E,0x7A3F,0x7F94,0x8003,0x80A1,0x818F,
0x82E6,0x82FD,0x83F0,0x85C1,0x8831,0x88B4,0x8AA5,0xF903,
0x8F9C,0x932E,0x96C7,0x9867,0x9AD8,0x9F13,0x54ED,0x659B,
0x66F2,0x688F,0x7A40,0x8C37,0x9D60,0x56F0,0x5764,0x5D11,
0x6606,0x68B1,0x68CD,0x6EFE,0x7428,0x889E,0x9BE4,0x6C68,
0xF904,0x9AA8,0x4F9B,0x516C,0x5171,0x529F,0x5B54,0x5DE5,
0x6050,0x606D,0x62F1,0x63A7,0x653B,0x73D9,0x7A7A,0x86A3,
0x8CA2,0x978F,0x4E32,0x5BE1,0x6208,0x679C,0x74DC,
       0x79D1,0x83D3,0x8A87,0x8AB2,0x8DE8,0x904E,0x934B,
0x9846,0x5ED3,0x69E8,0x85FF,0x90ED,0xF905,0x51A0,0x5B98,
0x5BEC,0x6163,0x68FA,0x6B3E,0x704C,0x742F,0x74D8,0x7BA1,
0x7F50,0x83C5,0x89C0,0x8CAB,0x95DC,0x9928,0x522E,0x605D,
0x62EC,0x9002,0x4F8A,0x5149,0x5321,0x58D9,0x5EE3,0x66E0,
0x6D38,0x709A,0x72C2,0x73D6,0x7B50,0x80F1,0x945B,0x5366,
0x639B,0x7F6B,0x4E56,0x5080,0x584A,0x58DE,0x602A,0x6127,
0x62D0,0x69D0,0x9B41,0x5B8F,0x7D18,0x80B1,0x8F5F,0x4EA4,
0x50D1,0x54AC,0x55AC,0x5B0C,0x5DA0,0x5DE7,0x652A,0x654E,
0x6821,0x6A4B,0x72E1,0x768E,0x77EF,0x7D5E,0x7FF9,0x81A0,
0x854E,0x86DF,0x8F03,0x8F4E,0x90CA,0x9903,0x9A55,0x9BAB,
0x4E18,0x4E45,0x4E5D,0x4EC7,0x4FF1,0x5177,0x52FE,
       0x5340,0x53E3,0x53E5,0x548E,0x5614,0x5775,0x57A2,
0x5BC7,0x5D87,0x5ED0,0x61FC,0x62D8,0x6551,0x67B8,0x67E9,
0x69CB,0x6B50,0x6BC6,0x6BEC,0x6C42,0x6E9D,0x7078,0x72D7,
0x7396,0x7403,0x77BF,0x77E9,0x7A76,0x7D7F,0x8009,0x81FC,
0x8205,0x820A,0x82DF,0x8862,0x8B33,0x8CFC,0x8EC0,0x9011,
0x90B1,0x9264,0x92B6,0x99D2,0x9A45,0x9CE9,0x9DD7,0x9F9C,
0x570B,0x5C40,0x83CA,0x97A0,0x97AB,0x9EB4,0x541B,0x7A98,
0x7FA4,0x88D9,0x8ECD,0x90E1,0x5800,0x5C48,0x6398,0x7A9F,
0x5BAE,0x5F13,0x7A79,0x7AAE,0x828E,0x8EAC,0x5026,0x5238,
0x52F8,0x5377,0x5708,0x62F3,0x6372,0x6B0A,0x6DC3,0x7737,
0x53A5,0x7357,0x8568,0x8E76,0x95D5,0x673A,0x6AC3,0x6F70,
0x8A6D,0x8ECC,0x994B,0xF906,0x6677,0x6B78,0x8CB4,
       0x9B3C,0xF907,0x53EB,0x572D,0x594E,0x63C6,0x69FB,
0x73EA,0x7845,0x7ABA,0x7AC5,0x7CFE,0x8475,0x898F,0x8D73,
0x9035,0x95A8,0x52FB,0x5747,0x7547,0x7B60,0x83CC,0x921E,
0xF908,0x6A58,0x514B,0x524B,0x5287,0x621F,0x68D8,0x6975,
0x9699,0x50C5,0x52A4,0x52E4,0x61C3,0x65A4,0x6839,0x69FF,
0x747E,0x7B4B,0x82B9,0x83EB,0x89B2,0x8B39,0x8FD1,0x9949,
0xF909,0x4ECA,0x5997,0x64D2,0x6611,0x6A8E,0x7434,0x7981,
0x79BD,0x82A9,0x887E,0x887F,0x895F,0xF90A,0x9326,0x4F0B,
0x53CA,0x6025,0x6271,0x6C72,0x7D1A,0x7D66,0x4E98,0x5162,
0x77DC,0x80AF,0x4F01,0x4F0E,0x5176,0x5180,0x55DC,0x5668,
0x573B,0x57FA,0x57FC,0x5914,0x5947,0x5993,0x5BC4,0x5C90,
0x5D0E,0x5DF1,0x5E7E,0x5FCC,0x6280,0x65D7,0x65E3,
       0x671E,0x671F,0x675E,0x68CB,0x68C4,0x6A5F,0x6B3A,
0x6C23,0x6C7D,0x6C82,0x6DC7,0x7398,0x7426,0x742A,0x7482,
0x74A3,0x7578,0x757F,0x7881,0x78EF,0x7941,0x7947,0x7948,
0x797A,0x7B95,0x7D00,0x7DBA,0x7F88,0x8006,0x802D,0x808C,
0x8A18,0x8B4F,0x8C48,0x8D77,0x9321,0x9324,0x98E2,0x9951,
0x9A0E,0x9A0F,0x9A65,0x9E92,0x7DCA,0x4F76,0x5409,0x62EE,
0x6854,0x91D1,0x55AB,0x513A,0xF90B,0xF90C,0x5A1C,0x61E6,
0xF90D,0x62CF,0x62FF,0xF90E,0xF90F,0xF910,0xF911,0xF912,
0xF913,0x90A3,0xF914,0xF915,0xF916,0xF917,0xF918,0x8AFE,
0xF919,0xF91A,0xF91B,0xF91C,0x6696,0xF91D,0x7156,0xF91E,
0xF91F,0x96E3,0xF920,0x634F,0x637A,0x5357,0xF921,0x678F,
0x6960,0x6E73,0xF922,0x7537,0xF923,0xF924,0xF925,
       0x7D0D,0xF926,0xF927,0x8872,0x56CA,0x5A18,0xF928,
0xF929,0xF92A,0xF92B,0xF92C,0x4E43,0xF92D,0x5167,0x5948,
0x67F0,0x8010,0xF92E,0x5973,0x5E74,0x649A,0x79CA,0x5FF5,
0x606C,0x62C8,0x637B,0x5BE7,0x5BD7,0x52AA,0xF92F,0x5974,
0x5F29,0x6012,0xF930,0xF931,0xF932,0x7459,0xF933,0xF934,
0xF935,0xF936,0xF937,0xF938,0x99D1,0xF939,0xF93A,0xF93B,
0xF93C,0xF93D,0xF93E,0xF93F,0xF940,0xF941,0xF942,0xF943,
0x6FC3,0xF944,0xF945,0x81BF,0x8FB2,0x60F1,0xF946,0xF947,
0x8166,0xF948,0xF949,0x5C3F,0xF94A,0xF94B,0xF94C,0xF94D,
0xF94E,0xF94F,0xF950,0xF951,0x5AE9,0x8A25,0x677B,0x7D10,
0xF952,0xF953,0xF954,0xF955,0xF956,0xF957,0x80FD,0xF958,
0xF959,0x5C3C,0x6CE5,0x533F,0x6EBA,0x591A,0x8336,
       0x4E39,0x4EB6,0x4F46,0x55AE,0x5718,0x58C7,0x5F56,
0x65B7,0x65E6,0x6A80,0x6BB5,0x6E4D,0x77ED,0x7AEF,0x7C1E,
0x7DDE,0x86CB,0x8892,0x9132,0x935B,0x64BB,0x6FBE,0x737A,
0x75B8,0x9054,0x5556,0x574D,0x61BA,0x64D4,0x66C7,0x6DE1,
0x6E5B,0x6F6D,0x6FB9,0x75F0,0x8043,0x81BD,0x8541,0x8983,
0x8AC7,0x8B5A,0x931F,0x6C93,0x7553,0x7B54,0x8E0F,0x905D,
0x5510,0x5802,0x5858,0x5E62,0x6207,0x649E,0x68E0,0x7576,
0x7CD6,0x87B3,0x9EE8,0x4EE3,0x5788,0x576E,0x5927,0x5C0D,
0x5CB1,0x5E36,0x5F85,0x6234,0x64E1,0x73B3,0x81FA,0x888B,
0x8CB8,0x968A,0x9EDB,0x5B85,0x5FB7,0x60B3,0x5012,0x5200,
0x5230,0x5716,0x5835,0x5857,0x5C0E,0x5C60,0x5CF6,0x5D8B,
0x5EA6,0x5F92,0x60BC,0x6311,0x6389,0x6417,0x6843,
       0x68F9,0x6AC2,0x6DD8,0x6E21,0x6ED4,0x6FE4,0x71FE,
0x76DC,0x7779,0x79B1,0x7A3B,0x8404,0x89A9,0x8CED,0x8DF3,
0x8E48,0x9003,0x9014,0x9053,0x90FD,0x934D,0x9676,0x97DC,
0x6BD2,0x7006,0x7258,0x72A2,0x7368,0x7763,0x79BF,0x7BE4,
0x7E9B,0x8B80,0x58A9,0x60C7,0x6566,0x65FD,0x66BE,0x6C8C,
0x711E,0x71C9,0x8C5A,0x9813,0x4E6D,0x7A81,0x4EDD,0x51AC,
0x51CD,0x52D5,0x540C,0x61A7,0x6771,0x6850,0x68DF,0x6D1E,
0x6F7C,0x75BC,0x77B3,0x7AE5,0x80F4,0x8463,0x9285,0x515C,
0x6597,0x675C,0x6793,0x75D8,0x7AC7,0x8373,0xF95A,0x8C46,
0x9017,0x982D,0x5C6F,0x81C0,0x829A,0x9041,0x906F,0x920D,
0x5F97,0x5D9D,0x6A59,0x71C8,0x767B,0x7B49,0x85E4,0x8B04,
0x9127,0x9A30,0x5587,0x61F6,0xF95B,0x7669,0x7F85,
       0x863F,0x87BA,0x88F8,0x908F,0xF95C,0x6D1B,0x70D9,
0x73DE,0x7D61,0x843D,0xF95D,0x916A,0x99F1,0xF95E,0x4E82,
0x5375,0x6B04,0x6B12,0x703E,0x721B,0x862D,0x9E1E,0x524C,
0x8FA3,0x5D50,0x64E5,0x652C,0x6B16,0x6FEB,0x7C43,0x7E9C,
0x85CD,0x8964,0x89BD,0x62C9,0x81D8,0x881F,0x5ECA,0x6717,
0x6D6A,0x72FC,0x7405,0x746F,0x8782,0x90DE,0x4F86,0x5D0D,
0x5FA0,0x840A,0x51B7,0x63A0,0x7565,0x4EAE,0x5006,0x5169,
0x51C9,0x6881,0x6A11,0x7CAE,0x7CB1,0x7CE7,0x826F,0x8AD2,
0x8F1B,0x91CF,0x4FB6,0x5137,0x52F5,0x5442,0x5EEC,0x616E,
0x623E,0x65C5,0x6ADA,0x6FFE,0x792A,0x85DC,0x8823,0x95AD,
0x9A62,0x9A6A,0x9E97,0x9ECE,0x529B,0x66C6,0x6B77,0x701D,
0x792B,0x8F62,0x9742,0x6190,0x6200,0x6523,0x6F23,
       0x7149,0x7489,0x7DF4,0x806F,0x84EE,0x8F26,0x9023,
0x934A,0x51BD,0x5217,0x52A3,0x6D0C,0x70C8,0x88C2,0x5EC9,
0x6582,0x6BAE,0x6FC2,0x7C3E,0x7375,0x4EE4,0x4F36,0x56F9,
0xF95F,0x5CBA,0x5DBA,0x601C,0x73B2,0x7B2D,0x7F9A,0x7FCE,
0x8046,0x901E,0x9234,0x96F6,0x9748,0x9818,0x9F61,0x4F8B,
0x6FA7,0x79AE,0x91B4,0x96B7,0x52DE,0xF960,0x6488,0x64C4,
0x6AD3,0x6F5E,0x7018,0x7210,0x76E7,0x8001,0x8606,0x865C,
0x8DEF,0x8F05,0x9732,0x9B6F,0x9DFA,0x9E75,0x788C,0x797F,
0x7DA0,0x83C9,0x9304,0x9E7F,0x9E93,0x8AD6,0x58DF,0x5F04,
0x6727,0x7027,0x74CF,0x7C60,0x807E,0x5121,0x7028,0x7262,
0x78CA,0x8CC2,0x8CDA,0x8CF4,0x96F7,0x4E86,0x50DA,0x5BEE,
0x5ED6,0x6599,0x71CE,0x7642,0x77AD,0x804A,0x84FC,
       0x907C,0x9B27,0x9F8D,0x58D8,0x5A41,0x5C62,0x6A13,
0x6DDA,0x6F0F,0x763B,0x7D2F,0x7E37,0x851E,0x8938,0x93E4,
0x964B,0x5289,0x65D2,0x67F3,0x69B4,0x6D41,0x6E9C,0x700F,
0x7409,0x7460,0x7559,0x7624,0x786B,0x8B2C,0x985E,0x516D,
0x622E,0x9678,0x4F96,0x502B,0x5D19,0x6DEA,0x7DB8,0x8F2A,
0x5F8B,0x6144,0x6817,0xF961,0x9686,0x52D2,0x808B,0x51DC,
0x51CC,0x695E,0x7A1C,0x7DBE,0x83F1,0x9675,0x4FDA,0x5229,
0x5398,0x540F,0x550E,0x5C65,0x60A7,0x674E,0x68A8,0x6D6C,
0x7281,0x72F8,0x7406,0x7483,0xF962,0x75E2,0x7C6C,0x7F79,
0x7FB8,0x8389,0x88CF,0x88E1,0x91CC,0x91D0,0x96E2,0x9BC9,
0x541D,0x6F7E,0x71D0,0x7498,0x85FA,0x8EAA,0x96A3,0x9C57,
0x9E9F,0x6797,0x6DCB,0x7433,0x81E8,0x9716,0x782C,
       0x7ACB,0x7B20,0x7C92,0x6469,0x746A,0x75F2,0x78BC,
0x78E8,0x99AC,0x9B54,0x9EBB,0x5BDE,0x5E55,0x6F20,0x819C,
0x83AB,0x9088,0x4E07,0x534D,0x5A29,0x5DD2,0x5F4E,0x6162,
0x633D,0x6669,0x66FC,0x6EFF,0x6F2B,0x7063,0x779E,0x842C,
0x8513,0x883B,0x8F13,0x9945,0x9C3B,0x551C,0x62B9,0x672B,
0x6CAB,0x8309,0x896A,0x977A,0x4EA1,0x5984,0x5FD8,0x5FD9,
0x671B,0x7DB2,0x7F54,0x8292,0x832B,0x83BD,0x8F1E,0x9099,
0x57CB,0x59B9,0x5A92,0x5BD0,0x6627,0x679A,0x6885,0x6BCF,
0x7164,0x7F75,0x8CB7,0x8CE3,0x9081,0x9B45,0x8108,0x8C8A,
0x964C,0x9A40,0x9EA5,0x5B5F,0x6C13,0x731B,0x76F2,0x76DF,
0x840C,0x51AA,0x8993,0x514D,0x5195,0x52C9,0x68C9,0x6C94,
0x7704,0x7720,0x7DBF,0x7DEC,0x9762,0x9EB5,0x6EC5,
       0x8511,0x51A5,0x540D,0x547D,0x660E,0x669D,0x6927,
0x6E9F,0x76BF,0x7791,0x8317,0x84C2,0x879F,0x9169,0x9298,
0x9CF4,0x8882,0x4FAE,0x5192,0x52DF,0x59C6,0x5E3D,0x6155,
0x6478,0x6479,0x66AE,0x67D0,0x6A21,0x6BCD,0x6BDB,0x725F,
0x7261,0x7441,0x7738,0x77DB,0x8017,0x82BC,0x8305,0x8B00,
0x8B28,0x8C8C,0x6728,0x6C90,0x7267,0x76EE,0x7766,0x7A46,
0x9DA9,0x6B7F,0x6C92,0x5922,0x6726,0x8499,0x536F,0x5893,
0x5999,0x5EDF,0x63CF,0x6634,0x6773,0x6E3A,0x732B,0x7AD7,
0x82D7,0x9328,0x52D9,0x5DEB,0x61AE,0x61CB,0x620A,0x62C7,
0x64AB,0x65E0,0x6959,0x6B66,0x6BCB,0x7121,0x73F7,0x755D,
0x7E46,0x821E,0x8302,0x856A,0x8AA3,0x8CBF,0x9727,0x9D61,
0x58A8,0x9ED8,0x5011,0x520E,0x543B,0x554F,0x6587,
       0x6C76,0x7D0A,0x7D0B,0x805E,0x868A,0x9580,0x96EF,
0x52FF,0x6C95,0x7269,0x5473,0x5A9A,0x5C3E,0x5D4B,0x5F4C,
0x5FAE,0x672A,0x68B6,0x6963,0x6E3C,0x6E44,0x7709,0x7C73,
0x7F8E,0x8587,0x8B0E,0x8FF7,0x9761,0x9EF4,0x5CB7,0x60B6,
0x610D,0x61AB,0x654F,0x65FB,0x65FC,0x6C11,0x6CEF,0x739F,
0x73C9,0x7DE1,0x9594,0x5BC6,0x871C,0x8B10,0x525D,0x535A,
0x62CD,0x640F,0x64B2,0x6734,0x6A38,0x6CCA,0x73C0,0x749E,
0x7B94,0x7C95,0x7E1B,0x818A,0x8236,0x8584,0x8FEB,0x96F9,
0x99C1,0x4F34,0x534A,0x53CD,0x53DB,0x62CC,0x642C,0x6500,
0x6591,0x69C3,0x6CEE,0x6F58,0x73ED,0x7554,0x7622,0x76E4,
0x76FC,0x78D0,0x78FB,0x792C,0x7D46,0x822C,0x87E0,0x8FD4,
0x9812,0x98EF,0x52C3,0x62D4,0x64A5,0x6E24,0x6F51,
       0x767C,0x8DCB,0x91B1,0x9262,0x9AEE,0x9B43,0x5023,
0x508D,0x574A,0x59A8,0x5C28,0x5E47,0x5F77,0x623F,0x653E,
0x65B9,0x65C1,0x6609,0x678B,0x699C,0x6EC2,0x78C5,0x7D21,
0x80AA,0x8180,0x822B,0x82B3,0x84A1,0x868C,0x8A2A,0x8B17,
0x90A6,0x9632,0x9F90,0x500D,0x4FF3,0xF963,0x57F9,0x5F98,
0x62DC,0x6392,0x676F,0x6E43,0x7119,0x76C3,0x80CC,0x80DA,
0x88F4,0x88F5,0x8919,0x8CE0,0x8F29,0x914D,0x966A,0x4F2F,
0x4F70,0x5E1B,0x67CF,0x6822,0x767D,0x767E,0x9B44,0x5E61,
0x6A0A,0x7169,0x71D4,0x756A,0xF964,0x7E41,0x8543,0x85E9,
0x98DC,0x4F10,0x7B4F,0x7F70,0x95A5,0x51E1,0x5E06,0x68B5,
0x6C3E,0x6C4E,0x6CDB,0x72AF,0x7BC4,0x8303,0x6CD5,0x743A,
0x50FB,0x5288,0x58C1,0x64D8,0x6A97,0x74A7,0x7656,
       0x78A7,0x8617,0x95E2,0x9739,0xF965,0x535E,0x5F01,
0x8B8A,0x8FA8,0x8FAF,0x908A,0x5225,0x77A5,0x9C49,0x9F08,
0x4E19,0x5002,0x5175,0x5C5B,0x5E77,0x661E,0x663A,0x67C4,
0x68C5,0x70B3,0x7501,0x75C5,0x79C9,0x7ADD,0x8F27,0x9920,
0x9A08,0x4FDD,0x5821,0x5831,0x5BF6,0x666E,0x6B65,0x6D11,
0x6E7A,0x6F7D,0x73E4,0x752B,0x83E9,0x88DC,0x8913,0x8B5C,
0x8F14,0x4F0F,0x50D5,0x5310,0x535C,0x5B93,0x5FA9,0x670D,
0x798F,0x8179,0x832F,0x8514,0x8907,0x8986,0x8F39,0x8F3B,
0x99A5,0x9C12,0x672C,0x4E76,0x4FF8,0x5949,0x5C01,0x5CEF,
0x5CF0,0x6367,0x68D2,0x70FD,0x71A2,0x742B,0x7E2B,0x84EC,
0x8702,0x9022,0x92D2,0x9CF3,0x4E0D,0x4ED8,0x4FEF,0x5085,
0x5256,0x526F,0x5426,0x5490,0x57E0,0x592B,0x5A66,
       0x5B5A,0x5B75,0x5BCC,0x5E9C,0xF966,0x6276,0x6577,
0x65A7,0x6D6E,0x6EA5,0x7236,0x7B26,0x7C3F,0x7F36,0x8150,
0x8151,0x819A,0x8240,0x8299,0x83A9,0x8A03,0x8CA0,0x8CE6,
0x8CFB,0x8D74,0x8DBA,0x90E8,0x91DC,0x961C,0x9644,0x99D9,
0x9CE7,0x5317,0x5206,0x5429,0x5674,0x58B3,0x5954,0x596E,
0x5FFF,0x61A4,0x626E,0x6610,0x6C7E,0x711A,0x76C6,0x7C89,
0x7CDE,0x7D1B,0x82AC,0x8CC1,0x96F0,0xF967,0x4F5B,0x5F17,
0x5F7F,0x62C2,0x5D29,0x670B,0x68DA,0x787C,0x7E43,0x9D6C,
0x4E15,0x5099,0x5315,0x532A,0x5351,0x5983,0x5A62,0x5E87,
0x60B2,0x618A,0x6249,0x6279,0x6590,0x6787,0x69A7,0x6BD4,
0x6BD6,0x6BD7,0x6BD8,0x6CB8,0xF968,0x7435,0x75FA,0x7812,
0x7891,0x79D5,0x79D8,0x7C83,0x7DCB,0x7FE1,0x80A5,
       0x813E,0x81C2,0x83F2,0x871A,0x88E8,0x8AB9,0x8B6C,
0x8CBB,0x9119,0x975E,0x98DB,0x9F3B,0x56AC,0x5B2A,0x5F6C,
0x658C,0x6AB3,0x6BAF,0x6D5C,0x6FF1,0x7015,0x725D,0x73AD,
0x8CA7,0x8CD3,0x983B,0x6191,0x6C37,0x8058,0x9A01,0x4E4D,
0x4E8B,0x4E9B,0x4ED5,0x4F3A,0x4F3C,0x4F7F,0x4FDF,0x50FF,
0x53F2,0x53F8,0x5506,0x55E3,0x56DB,0x58EB,0x5962,0x5A11,
0x5BEB,0x5BFA,0x5C04,0x5DF3,0x5E2B,0x5F99,0x601D,0x6368,
0x659C,0x65AF,0x67F6,0x67FB,0x68AD,0x6B7B,0x6C99,0x6CD7,
0x6E23,0x7009,0x7345,0x7802,0x793E,0x7940,0x7960,0x79C1,
0x7BE9,0x7D17,0x7D72,0x8086,0x820D,0x838E,0x84D1,0x86C7,
0x88DF,0x8A50,0x8A5E,0x8B1D,0x8CDC,0x8D66,0x8FAD,0x90AA,
0x98FC,0x99DF,0x9E9D,0x524A,0xF969,0x6714,0xF96A,
       0x5098,0x522A,0x5C71,0x6563,0x6C55,0x73CA,0x7523,
0x759D,0x7B97,0x849C,0x9178,0x9730,0x4E77,0x6492,0x6BBA,
0x715E,0x85A9,0x4E09,0xF96B,0x6749,0x68EE,0x6E17,0x829F,
0x8518,0x886B,0x63F7,0x6F81,0x9212,0x98AF,0x4E0A,0x50B7,
0x50CF,0x511F,0x5546,0x55AA,0x5617,0x5B40,0x5C19,0x5CE0,
0x5E38,0x5E8A,0x5EA0,0x5EC2,0x60F3,0x6851,0x6A61,0x6E58,
0x723D,0x7240,0x72C0,0x76F8,0x7965,0x7BB1,0x7FD4,0x88F3,
0x89F4,0x8A73,0x8C61,0x8CDE,0x971C,0x585E,0x74BD,0x8CFD,
0x55C7,0xF96C,0x7A61,0x7D22,0x8272,0x7272,0x751F,0x7525,
0xF96D,0x7B19,0x5885,0x58FB,0x5DBC,0x5E8F,0x5EB6,0x5F90,
0x6055,0x6292,0x637F,0x654D,0x6691,0x66D9,0x66F8,0x6816,
0x68F2,0x7280,0x745E,0x7B6E,0x7D6E,0x7DD6,0x7F72,
       0x80E5,0x8212,0x85AF,0x897F,0x8A93,0x901D,0x92E4,
0x9ECD,0x9F20,0x5915,0x596D,0x5E2D,0x60DC,0x6614,0x6673,
0x6790,0x6C50,0x6DC5,0x6F5F,0x77F3,0x78A9,0x84C6,0x91CB,
0x932B,0x4ED9,0x50CA,0x5148,0x5584,0x5B0B,0x5BA3,0x6247,
0x657E,0x65CB,0x6E32,0x717D,0x7401,0x7444,0x7487,0x74BF,
0x766C,0x79AA,0x7DDA,0x7E55,0x7FA8,0x817A,0x81B3,0x8239,
0x861A,0x87EC,0x8A75,0x8DE3,0x9078,0x9291,0x9425,0x994D,
0x9BAE,0x5368,0x5C51,0x6954,0x6CC4,0x6D29,0x6E2B,0x820C,
0x859B,0x893B,0x8A2D,0x8AAA,0x96EA,0x9F67,0x5261,0x66B9,
0x6BB2,0x7E96,0x87FE,0x8D0D,0x9583,0x965D,0x651D,0x6D89,
0x71EE,0xF96E,0x57CE,0x59D3,0x5BAC,0x6027,0x60FA,0x6210,
0x661F,0x665F,0x7329,0x73F9,0x76DB,0x7701,0x7B6C,
       0x8056,0x8072,0x8165,0x8AA0,0x9192,0x4E16,0x52E2,
0x6B72,0x6D17,0x7A05,0x7B39,0x7D30,0xF96F,0x8CB0,0x53EC,
0x562F,0x5851,0x5BB5,0x5C0F,0x5C11,0x5DE2,0x6240,0x6383,
0x6414,0x662D,0x68B3,0x6CBC,0x6D88,0x6EAF,0x701F,0x70A4,
0x71D2,0x7526,0x758F,0x758E,0x7619,0x7B11,0x7BE0,0x7C2B,
0x7D20,0x7D39,0x852C,0x856D,0x8607,0x8A34,0x900D,0x9061,
0x90B5,0x92B7,0x97F6,0x9A37,0x4FD7,0x5C6C,0x675F,0x6D91,
0x7C9F,0x7E8C,0x8B16,0x8D16,0x901F,0x5B6B,0x5DFD,0x640D,
0x84C0,0x905C,0x98E1,0x7387,0x5B8B,0x609A,0x677E,0x6DDE,
0x8A1F,0x8AA6,0x9001,0x980C,0x5237,0xF970,0x7051,0x788E,
0x9396,0x8870,0x91D7,0x4FEE,0x53D7,0x55FD,0x56DA,0x5782,
0x58FD,0x5AC2,0x5B88,0x5CAB,0x5CC0,0x5E25,0x6101,
       0x620D,0x624B,0x6388,0x641C,0x6536,0x6578,0x6A39,
0x6B8A,0x6C34,0x6D19,0x6F31,0x71E7,0x72E9,0x7378,0x7407,
0x74B2,0x7626,0x7761,0x79C0,0x7A57,0x7AEA,0x7CB9,0x7D8F,
0x7DAC,0x7E61,0x7F9E,0x8129,0x8331,0x8490,0x84DA,0x85EA,
0x8896,0x8AB0,0x8B90,0x8F38,0x9042,0x9083,0x916C,0x9296,
0x92B9,0x968B,0x96A7,0x96A8,0x96D6,0x9700,0x9808,0x9996,
0x9AD3,0x9B1A,0x53D4,0x587E,0x5919,0x5B70,0x5BBF,0x6DD1,
0x6F5A,0x719F,0x7421,0x74B9,0x8085,0x83FD,0x5DE1,0x5F87,
0x5FAA,0x6042,0x65EC,0x6812,0x696F,0x6A53,0x6B89,0x6D35,
0x6DF3,0x73E3,0x76FE,0x77AC,0x7B4D,0x7D14,0x8123,0x821C,
0x8340,0x84F4,0x8563,0x8A62,0x8AC4,0x9187,0x931E,0x9806,
0x99B4,0x620C,0x8853,0x8FF0,0x9265,0x5D07,0x5D27,
       0x5D69,0x745F,0x819D,0x8768,0x6FD5,0x62FE,0x7FD2,
0x8936,0x8972,0x4E1E,0x4E58,0x50E7,0x52DD,0x5347,0x627F,
0x6607,0x7E69,0x8805,0x965E,0x4F8D,0x5319,0x5636,0x59CB,
0x5AA4,0x5C38,0x5C4E,0x5C4D,0x5E02,0x5F11,0x6043,0x65BD,
0x662F,0x6642,0x67BE,0x67F4,0x731C,0x77E2,0x793A,0x7FC5,
0x8494,0x84CD,0x8996,0x8A66,0x8A69,0x8AE1,0x8C55,0x8C7A,
0x57F4,0x5BD4,0x5F0F,0x606F,0x62ED,0x690D,0x6B96,0x6E5C,
0x7184,0x7BD2,0x8755,0x8B58,0x8EFE,0x98DF,0x98FE,0x4F38,
0x4F81,0x4FE1,0x547B,0x5A20,0x5BB8,0x613C,0x65B0,0x6668,
0x71FC,0x7533,0x795E,0x7D33,0x814E,0x81E3,0x8398,0x85AA,
0x85CE,0x8703,0x8A0A,0x8EAB,0x8F9B,0xF971,0x8FC5,0x5931,
0x5BA4,0x5BE6,0x6089,0x5BE9,0x5C0B,0x5FC3,0x6C81,
       0xF972,0x6DF1,0x700B,0x751A,0x82AF,0x8AF6,0x4EC0,
0x5341,0xF973,0x96D9,0x6C0F,0x4E9E,0x4FC4,0x5152,0x555E,
0x5A25,0x5CE8,0x6211,0x7259,0x82BD,0x83AA,0x86FE,0x8859,
0x8A1D,0x963F,0x96C5,0x9913,0x9D09,0x9D5D,0x580A,0x5CB3,
0x5DBD,0x5E44,0x60E1,0x6115,0x63E1,0x6A02,0x6E25,0x9102,
0x9354,0x984E,0x9C10,0x9F77,0x5B89,0x5CB8,0x6309,0x664F,
0x6848,0x773C,0x96C1,0x978D,0x9854,0x9B9F,0x65A1,0x8B01,
0x8ECB,0x95BC,0x5535,0x5CA9,0x5DD6,0x5EB5,0x6697,0x764C,
0x83F4,0x95C7,0x58D3,0x62BC,0x72CE,0x9D28,0x4EF0,0x592E,
0x600F,0x663B,0x6B83,0x79E7,0x9D26,0x5393,0x54C0,0x57C3,
0x5D16,0x611B,0x66D6,0x6DAF,0x788D,0x827E,0x9698,0x9744,
0x5384,0x627C,0x6396,0x6DB2,0x7E0A,0x814B,0x984D,
       0x6AFB,0x7F4C,0x9DAF,0x9E1A,0x4E5F,0x503B,0x51B6,
0x591C,0x60F9,0x63F6,0x6930,0x723A,0x8036,0xF974,0x91CE,
0x5F31,0xF975,0xF976,0x7D04,0x82E5,0x846F,0x84BB,0x85E5,
0x8E8D,0xF977,0x4F6F,0xF978,0xF979,0x58E4,0x5B43,0x6059,
0x63DA,0x6518,0x656D,0x6698,0xF97A,0x694A,0x6A23,0x6D0B,
0x7001,0x716C,0x75D2,0x760D,0x79B3,0x7A70,0xF97B,0x7F8A,
0xF97C,0x8944,0xF97D,0x8B93,0x91C0,0x967D,0xF97E,0x990A,
0x5704,0x5FA1,0x65BC,0x6F01,0x7600,0x79A6,0x8A9E,0x99AD,
0x9B5A,0x9F6C,0x5104,0x61B6,0x6291,0x6A8D,0x81C6,0x5043,
0x5830,0x5F66,0x7109,0x8A00,0x8AFA,0x5B7C,0x8616,0x4FFA,
0x513C,0x56B4,0x5944,0x63A9,0x6DF9,0x5DAA,0x696D,0x5186,
0x4E88,0x4F59,0xF97F,0xF980,0xF981,0x5982,0xF982,
       0xF983,0x6B5F,0x6C5D,0xF984,0x74B5,0x7916,0xF985,
0x8207,0x8245,0x8339,0x8F3F,0x8F5D,0xF986,0x9918,0xF987,
0xF988,0xF989,0x4EA6,0xF98A,0x57DF,0x5F79,0x6613,0xF98B,
0xF98C,0x75AB,0x7E79,0x8B6F,0xF98D,0x9006,0x9A5B,0x56A5,
0x5827,0x59F8,0x5A1F,0x5BB4,0xF98E,0x5EF6,0xF98F,0xF990,
0x6350,0x633B,0xF991,0x693D,0x6C87,0x6CBF,0x6D8E,0x6D93,
0x6DF5,0x6F14,0xF992,0x70DF,0x7136,0x7159,0xF993,0x71C3,
0x71D5,0xF994,0x784F,0x786F,0xF995,0x7B75,0x7DE3,0xF996,
0x7E2F,0xF997,0x884D,0x8EDF,0xF998,0xF999,0xF99A,0x925B,
0xF99B,0x9CF6,0xF99C,0xF99D,0xF99E,0x6085,0x6D85,0xF99F,
0x71B1,0xF9A0,0xF9A1,0x95B1,0x53AD,0xF9A2,0xF9A3,0xF9A4,
0x67D3,0xF9A5,0x708E,0x7130,0x7430,0x8276,0x82D2,
       0xF9A6,0x95BB,0x9AE5,0x9E7D,0x66C4,0xF9A7,0x71C1,
0x8449,0xF9A8,0xF9A9,0x584B,0xF9AA,0xF9AB,0x5DB8,0x5F71,
0xF9AC,0x6620,0x668E,0x6979,0x69AE,0x6C38,0x6CF3,0x6E36,
0x6F41,0x6FDA,0x701B,0x702F,0x7150,0x71DF,0x7370,0xF9AD,
0x745B,0xF9AE,0x74D4,0x76C8,0x7A4E,0x7E93,0xF9AF,0xF9B0,
0x82F1,0x8A60,0x8FCE,0xF9B1,0x9348,0xF9B2,0x9719,0xF9B3,
0xF9B4,0x4E42,0x502A,0xF9B5,0x5208,0x53E1,0x66F3,0x6C6D,
0x6FCA,0x730A,0x777F,0x7A62,0x82AE,0x85DD,0x8602,0xF9B6,
0x88D4,0x8A63,0x8B7D,0x8C6B,0xF9B7,0x92B3,0xF9B8,0x9713,
0x9810,0x4E94,0x4F0D,0x4FC9,0x50B2,0x5348,0x543E,0x5433,
0x55DA,0x5862,0x58BA,0x5967,0x5A1B,0x5BE4,0x609F,0xF9B9,
0x61CA,0x6556,0x65FF,0x6664,0x68A7,0x6C5A,0x6FB3,
       0x70CF,0x71AC,0x7352,0x7B7D,0x8708,0x8AA4,0x9C32,
0x9F07,0x5C4B,0x6C83,0x7344,0x7389,0x923A,0x6EAB,0x7465,
0x761F,0x7A69,0x7E15,0x860A,0x5140,0x58C5,0x64C1,0x74EE,
0x7515,0x7670,0x7FC1,0x9095,0x96CD,0x9954,0x6E26,0x74E6,
0x7AA9,0x7AAA,0x81E5,0x86D9,0x8778,0x8A1B,0x5A49,0x5B8C,
0x5B9B,0x68A1,0x6900,0x6D63,0x73A9,0x7413,0x742C,0x7897,
0x7DE9,0x7FEB,0x8118,0x8155,0x839E,0x8C4C,0x962E,0x9811,
0x66F0,0x5F80,0x65FA,0x6789,0x6C6A,0x738B,0x502D,0x5A03,
0x6B6A,0x77EE,0x5916,0x5D6C,0x5DCD,0x7325,0x754F,0xF9BA,
0xF9BB,0x50E5,0x51F9,0x582F,0x592D,0x5996,0x59DA,0x5BE5,
0xF9BC,0xF9BD,0x5DA2,0x62D7,0x6416,0x6493,0x64FE,0xF9BE,
0x66DC,0xF9BF,0x6A48,0xF9C0,0x71FF,0x7464,0xF9C1,
       0x7A88,0x7AAF,0x7E47,0x7E5E,0x8000,0x8170,0xF9C2,
0x87EF,0x8981,0x8B20,0x9059,0xF9C3,0x9080,0x9952,0x617E,
0x6B32,0x6D74,0x7E1F,0x8925,0x8FB1,0x4FD1,0x50AD,0x5197,
0x52C7,0x57C7,0x5889,0x5BB9,0x5EB8,0x6142,0x6995,0x6D8C,
0x6E67,0x6EB6,0x7194,0x7462,0x7528,0x752C,0x8073,0x8338,
0x84C9,0x8E0A,0x9394,0x93DE,0xF9C4,0x4E8E,0x4F51,0x5076,
0x512A,0x53C8,0x53CB,0x53F3,0x5B87,0x5BD3,0x5C24,0x611A,
0x6182,0x65F4,0x725B,0x7397,0x7440,0x76C2,0x7950,0x7991,
0x79B9,0x7D06,0x7FBD,0x828B,0x85D5,0x865E,0x8FC2,0x9047,
0x90F5,0x91EA,0x9685,0x96E8,0x96E9,0x52D6,0x5F67,0x65ED,
0x6631,0x682F,0x715C,0x7A36,0x90C1,0x980A,0x4E91,0xF9C5,
0x6A52,0x6B9E,0x6F90,0x7189,0x8018,0x82B8,0x8553,
       0x904B,0x9695,0x96F2,0x97FB,0x851A,0x9B31,0x4E90,
0x718A,0x96C4,0x5143,0x539F,0x54E1,0x5713,0x5712,0x57A3,
0x5A9B,0x5AC4,0x5BC3,0x6028,0x613F,0x63F4,0x6C85,0x6D39,
0x6E72,0x6E90,0x7230,0x733F,0x7457,0x82D1,0x8881,0x8F45,
0x9060,0xF9C6,0x9662,0x9858,0x9D1B,0x6708,0x8D8A,0x925E,
0x4F4D,0x5049,0x50DE,0x5371,0x570D,0x59D4,0x5A01,0x5C09,
0x6170,0x6690,0x6E2D,0x7232,0x744B,0x7DEF,0x80C3,0x840E,
0x8466,0x853F,0x875F,0x885B,0x8918,0x8B02,0x9055,0x97CB,
0x9B4F,0x4E73,0x4F91,0x5112,0x516A,0xF9C7,0x552F,0x55A9,
0x5B7A,0x5BA5,0x5E7C,0x5E7D,0x5EBE,0x60A0,0x60DF,0x6108,
0x6109,0x63C4,0x6538,0x6709,0xF9C8,0x67D4,0x67DA,0xF9C9,
0x6961,0x6962,0x6CB9,0x6D27,0xF9CA,0x6E38,0xF9CB,
       0x6FE1,0x7336,0x7337,0xF9CC,0x745C,0x7531,0xF9CD,
0x7652,0xF9CE,0xF9CF,0x7DAD,0x81FE,0x8438,0x88D5,0x8A98,
0x8ADB,0x8AED,0x8E30,0x8E42,0x904A,0x903E,0x907A,0x9149,
0x91C9,0x936E,0xF9D0,0xF9D1,0x5809,0xF9D2,0x6BD3,0x8089,
0x80B2,0xF9D3,0xF9D4,0x5141,0x596B,0x5C39,0xF9D5,0xF9D6,
0x6F64,0x73A7,0x80E4,0x8D07,0xF9D7,0x9217,0x958F,0xF9D8,
0xF9D9,0xF9DA,0xF9DB,0x807F,0x620E,0x701C,0x7D68,0x878D,
0xF9DC,0x57A0,0x6069,0x6147,0x6BB7,0x8ABE,0x9280,0x96B1,
0x4E59,0x541F,0x6DEB,0x852D,0x9670,0x97F3,0x98EE,0x63D6,
0x6CE3,0x9091,0x51DD,0x61C9,0x81BA,0x9DF9,0x4F9D,0x501A,
0x5100,0x5B9C,0x610F,0x61FF,0x64EC,0x6905,0x6BC5,0x7591,
0x77E3,0x7FA9,0x8264,0x858F,0x87FB,0x8863,0x8ABC,
       0x8B70,0x91AB,0x4E8C,0x4EE5,0x4F0A,0xF9DD,0xF9DE,
0x5937,0x59E8,0xF9DF,0x5DF2,0x5F1B,0x5F5B,0x6021,0xF9E0,
0xF9E1,0xF9E2,0xF9E3,0x723E,0x73E5,0xF9E4,0x7570,0x75CD,
0xF9E5,0x79FB,0xF9E6,0x800C,0x8033,0x8084,0x82E1,0x8351,
0xF9E7,0xF9E8,0x8CBD,0x8CB3,0x9087,0xF9E9,0xF9EA,0x98F4,
0x990C,0xF9EB,0xF9EC,0x7037,0x76CA,0x7FCA,0x7FCC,0x7FFC,
0x8B1A,0x4EBA,0x4EC1,0x5203,0x5370,0xF9ED,0x54BD,0x56E0,
0x59FB,0x5BC5,0x5F15,0x5FCD,0x6E6E,0xF9EE,0xF9EF,0x7D6A,
0x8335,0xF9F0,0x8693,0x8A8D,0xF9F1,0x976D,0x9777,0xF9F2,
0xF9F3,0x4E00,0x4F5A,0x4F7E,0x58F9,0x65E5,0x6EA2,0x9038,
0x93B0,0x99B9,0x4EFB,0x58EC,0x598A,0x59D9,0x6041,0xF9F4,
0xF9F5,0x7A14,0xF9F6,0x834F,0x8CC3,0x5165,0x5344,
       0xF9F7,0xF9F8,0xF9F9,0x4ECD,0x5269,0x5B55,0x82BF,
0x4ED4,0x523A,0x54A8,0x59C9,0x59FF,0x5B50,0x5B57,0x5B5C,
0x6063,0x6148,0x6ECB,0x7099,0x716E,0x7386,0x74F7,0x75B5,
0x78C1,0x7D2B,0x8005,0x81EA,0x8328,0x8517,0x85C9,0x8AEE,
0x8CC7,0x96CC,0x4F5C,0x52FA,0x56BC,0x65AB,0x6628,0x707C,
0x70B8,0x7235,0x7DBD,0x828D,0x914C,0x96C0,0x9D72,0x5B71,
0x68E7,0x6B98,0x6F7A,0x76DE,0x5C91,0x66AB,0x6F5B,0x7BB4,
0x7C2A,0x8836,0x96DC,0x4E08,0x4ED7,0x5320,0x5834,0x58BB,
0x58EF,0x596C,0x5C07,0x5E33,0x5E84,0x5F35,0x638C,0x66B2,
0x6756,0x6A1F,0x6AA3,0x6B0C,0x6F3F,0x7246,0xF9FA,0x7350,
0x748B,0x7AE0,0x7CA7,0x8178,0x81DF,0x81E7,0x838A,0x846C,
0x8523,0x8594,0x85CF,0x88DD,0x8D13,0x91AC,0x9577,
       0x969C,0x518D,0x54C9,0x5728,0x5BB0,0x624D,0x6750,
0x683D,0x6893,0x6E3D,0x6ED3,0x707D,0x7E21,0x88C1,0x8CA1,
0x8F09,0x9F4B,0x9F4E,0x722D,0x7B8F,0x8ACD,0x931A,0x4F47,
0x4F4E,0x5132,0x5480,0x59D0,0x5E95,0x62B5,0x6775,0x696E,
0x6A17,0x6CAE,0x6E1A,0x72D9,0x732A,0x75BD,0x7BB8,0x7D35,
0x82E7,0x83F9,0x8457,0x85F7,0x8A5B,0x8CAF,0x8E87,0x9019,
0x90B8,0x96CE,0x9F5F,0x52E3,0x540A,0x5AE1,0x5BC2,0x6458,
0x6575,0x6EF4,0x72C4,0xF9FB,0x7684,0x7A4D,0x7B1B,0x7C4D,
0x7E3E,0x7FDF,0x837B,0x8B2B,0x8CCA,0x8D64,0x8DE1,0x8E5F,
0x8FEA,0x8FF9,0x9069,0x93D1,0x4F43,0x4F7A,0x50B3,0x5168,
0x5178,0x524D,0x526A,0x5861,0x587C,0x5960,0x5C08,0x5C55,
0x5EDB,0x609B,0x6230,0x6813,0x6BBF,0x6C08,0x6FB1,
       0x714E,0x7420,0x7530,0x7538,0x7551,0x7672,0x7B4C,
0x7B8B,0x7BAD,0x7BC6,0x7E8F,0x8A6E,0x8F3E,0x8F49,0x923F,
0x9293,0x9322,0x942B,0x96FB,0x985A,0x986B,0x991E,0x5207,
0x622A,0x6298,0x6D59,0x7664,0x7ACA,0x7BC0,0x7D76,0x5360,
0x5CBE,0x5E97,0x6F38,0x70B9,0x7C98,0x9711,0x9B8E,0x9EDE,
0x63A5,0x647A,0x8776,0x4E01,0x4E95,0x4EAD,0x505C,0x5075,
0x5448,0x59C3,0x5B9A,0x5E40,0x5EAD,0x5EF7,0x5F81,0x60C5,
0x633A,0x653F,0x6574,0x65CC,0x6676,0x6678,0x67FE,0x6968,
0x6A89,0x6B63,0x6C40,0x6DC0,0x6DE8,0x6E1F,0x6E5E,0x701E,
0x70A1,0x738E,0x73FD,0x753A,0x775B,0x7887,0x798E,0x7A0B,
0x7A7D,0x7CBE,0x7D8E,0x8247,0x8A02,0x8AEA,0x8C9E,0x912D,
0x914A,0x91D8,0x9266,0x92CC,0x9320,0x9706,0x9756,
       0x975C,0x9802,0x9F0E,0x5236,0x5291,0x557C,0x5824,
0x5E1D,0x5F1F,0x608C,0x63D0,0x68AF,0x6FDF,0x796D,0x7B2C,
0x81CD,0x85BA,0x88FD,0x8AF8,0x8E44,0x918D,0x9664,0x969B,
0x973D,0x984C,0x9F4A,0x4FCE,0x5146,0x51CB,0x52A9,0x5632,
0x5F14,0x5F6B,0x63AA,0x64CD,0x65E9,0x6641,0x66FA,0x66F9,
0x671D,0x689D,0x68D7,0x69FD,0x6F15,0x6F6E,0x7167,0x71E5,
0x722A,0x74AA,0x773A,0x7956,0x795A,0x79DF,0x7A20,0x7A95,
0x7C97,0x7CDF,0x7D44,0x7E70,0x8087,0x85FB,0x86A4,0x8A54,
0x8ABF,0x8D99,0x8E81,0x9020,0x906D,0x91E3,0x963B,0x96D5,
0x9CE5,0x65CF,0x7C07,0x8DB3,0x93C3,0x5B58,0x5C0A,0x5352,
0x62D9,0x731D,0x5027,0x5B97,0x5F9E,0x60B0,0x616B,0x68D5,
0x6DD9,0x742E,0x7A2E,0x7D42,0x7D9C,0x7E31,0x816B,
       0x8E2A,0x8E35,0x937E,0x9418,0x4F50,0x5750,0x5DE6,
0x5EA7,0x632B,0x7F6A,0x4E3B,0x4F4F,0x4F8F,0x505A,0x59DD,
0x80C4,0x546A,0x5468,0x55FE,0x594F,0x5B99,0x5DDE,0x5EDA,
0x665D,0x6731,0x67F1,0x682A,0x6CE8,0x6D32,0x6E4A,0x6F8D,
0x70B7,0x73E0,0x7587,0x7C4C,0x7D02,0x7D2C,0x7DA2,0x821F,
0x86DB,0x8A3B,0x8A85,0x8D70,0x8E8A,0x8F33,0x9031,0x914E,
0x9152,0x9444,0x99D0,0x7AF9,0x7CA5,0x4FCA,0x5101,0x51C6,
0x57C8,0x5BEF,0x5CFB,0x6659,0x6A3D,0x6D5A,0x6E96,0x6FEC,
0x710C,0x756F,0x7AE3,0x8822,0x9021,0x9075,0x96CB,0x99FF,
0x8301,0x4E2D,0x4EF2,0x8846,0x91CD,0x537D,0x6ADB,0x696B,
0x6C41,0x847A,0x589E,0x618E,0x66FE,0x62EF,0x70DD,0x7511,
0x75C7,0x7E52,0x84B8,0x8B49,0x8D08,0x4E4B,0x53EA,
       0x54AB,0x5730,0x5740,0x5FD7,0x6301,0x6307,0x646F,
0x652F,0x65E8,0x667A,0x679D,0x67B3,0x6B62,0x6C60,0x6C9A,
0x6F2C,0x77E5,0x7825,0x7949,0x7957,0x7D19,0x80A2,0x8102,
0x81F3,0x829D,0x82B7,0x8718,0x8A8C,0xF9FC,0x8D04,0x8DBE,
0x9072,0x76F4,0x7A19,0x7A37,0x7E54,0x8077,0x5507,0x55D4,
0x5875,0x632F,0x6422,0x6649,0x664B,0x686D,0x699B,0x6B84,
0x6D25,0x6EB1,0x73CD,0x7468,0x74A1,0x755B,0x75B9,0x76E1,
0x771E,0x778B,0x79E6,0x7E09,0x7E1D,0x81FB,0x852F,0x8897,
0x8A3A,0x8CD1,0x8EEB,0x8FB0,0x9032,0x93AD,0x9663,0x9673,
0x9707,0x4F84,0x53F1,0x59EA,0x5AC9,0x5E19,0x684E,0x74C6,
0x75BE,0x79E9,0x7A92,0x81A3,0x86ED,0x8CEA,0x8DCC,0x8FED,
0x659F,0x6715,0xF9FD,0x57F7,0x6F57,0x7DDD,0x8F2F,
       0x93F6,0x96C6,0x5FB5,0x61F2,0x6F84,0x4E14,0x4F98,
0x501F,0x53C9,0x55DF,0x5D6F,0x5DEE,0x6B21,0x6B64,0x78CB,
0x7B9A,0xF9FE,0x8E49,0x8ECA,0x906E,0x6349,0x643E,0x7740,
0x7A84,0x932F,0x947F,0x9F6A,0x64B0,0x6FAF,0x71E6,0x74A8,
0x74DA,0x7AC4,0x7C12,0x7E82,0x7CB2,0x7E98,0x8B9A,0x8D0A,
0x947D,0x9910,0x994C,0x5239,0x5BDF,0x64E6,0x672D,0x7D2E,
0x50ED,0x53C3,0x5879,0x6158,0x6159,0x61FA,0x65AC,0x7AD9,
0x8B92,0x8B96,0x5009,0x5021,0x5275,0x5531,0x5A3C,0x5EE0,
0x5F70,0x6134,0x655E,0x660C,0x6636,0x66A2,0x69CD,0x6EC4,
0x6F32,0x7316,0x7621,0x7A93,0x8139,0x8259,0x83D6,0x84BC,
0x50B5,0x57F0,0x5BC0,0x5BE8,0x5F69,0x63A1,0x7826,0x7DB5,
0x83DC,0x8521,0x91C7,0x91F5,0x518A,0x67F5,0x7B56,
       0x8CAC,0x51C4,0x59BB,0x60BD,0x8655,0x501C,0xF9FF,
0x5254,0x5C3A,0x617D,0x621A,0x62D3,0x64F2,0x65A5,0x6ECC,
0x7620,0x810A,0x8E60,0x965F,0x96BB,0x4EDF,0x5343,0x5598,
0x5929,0x5DDD,0x64C5,0x6CC9,0x6DFA,0x7394,0x7A7F,0x821B,
0x85A6,0x8CE4,0x8E10,0x9077,0x91E7,0x95E1,0x9621,0x97C6,
0x51F8,0x54F2,0x5586,0x5FB9,0x64A4,0x6F88,0x7DB4,0x8F1F,
0x8F4D,0x9435,0x50C9,0x5C16,0x6CBE,0x6DFB,0x751B,0x77BB,
0x7C3D,0x7C64,0x8A79,0x8AC2,0x581E,0x59BE,0x5E16,0x6377,
0x7252,0x758A,0x776B,0x8ADC,0x8CBC,0x8F12,0x5EF3,0x6674,
0x6DF8,0x807D,0x83C1,0x8ACB,0x9751,0x9BD6,0xFA00,0x5243,
0x66FF,0x6D95,0x6EEF,0x7DE0,0x8AE6,0x902E,0x905E,0x9AD4,
0x521D,0x527F,0x54E8,0x6194,0x6284,0x62DB,0x68A2,
       0x6912,0x695A,0x6A35,0x7092,0x7126,0x785D,0x7901,
0x790E,0x79D2,0x7A0D,0x8096,0x8278,0x82D5,0x8349,0x8549,
0x8C82,0x8D85,0x9162,0x918B,0x91AE,0x4FC3,0x56D1,0x71ED,
0x77D7,0x8700,0x89F8,0x5BF8,0x5FD6,0x6751,0x90A8,0x53E2,
0x585A,0x5BF5,0x60A4,0x6181,0x6460,0x7E3D,0x8070,0x8525,
0x9283,0x64AE,0x50AC,0x5D14,0x6700,0x589C,0x62BD,0x63A8,
0x690E,0x6978,0x6A1E,0x6E6B,0x76BA,0x79CB,0x82BB,0x8429,
0x8ACF,0x8DA8,0x8FFD,0x9112,0x914B,0x919C,0x9310,0x9318,
0x939A,0x96DB,0x9A36,0x9C0D,0x4E11,0x755C,0x795D,0x7AFA,
0x7B51,0x7BC9,0x7E2E,0x84C4,0x8E59,0x8E74,0x8EF8,0x9010,
0x6625,0x693F,0x7443,0x51FA,0x672E,0x9EDC,0x5145,0x5FE0,
0x6C96,0x87F2,0x885D,0x8877,0x60B4,0x81B5,0x8403,
       0x8D05,0x53D6,0x5439,0x5634,0x5A36,0x5C31,0x708A,
0x7FE0,0x805A,0x8106,0x81ED,0x8DA3,0x9189,0x9A5F,0x9DF2,
0x5074,0x4EC4,0x53A0,0x60FB,0x6E2C,0x5C64,0x4F88,0x5024,
0x55E4,0x5CD9,0x5E5F,0x6065,0x6894,0x6CBB,0x6DC4,0x71BE,
0x75D4,0x75F4,0x7661,0x7A1A,0x7A49,0x7DC7,0x7DFB,0x7F6E,
0x81F4,0x86A9,0x8F1C,0x96C9,0x99B3,0x9F52,0x5247,0x52C5,
0x98ED,0x89AA,0x4E03,0x67D2,0x6F06,0x4FB5,0x5BE2,0x6795,
0x6C88,0x6D78,0x741B,0x7827,0x91DD,0x937C,0x87C4,0x79E4,
0x7A31,0x5FEB,0x4ED6,0x54A4,0x553E,0x58AE,0x59A5,0x60F0,
0x6253,0x62D6,0x6736,0x6955,0x8235,0x9640,0x99B1,0x99DD,
0x502C,0x5353,0x5544,0x577C,0xFA01,0x6258,0xFA02,0x64E2,
0x666B,0x67DD,0x6FC1,0x6FEF,0x7422,0x7438,0x8A17,
       0x9438,0x5451,0x5606,0x5766,0x5F48,0x619A,0x6B4E,
0x7058,0x70AD,0x7DBB,0x8A95,0x596A,0x812B,0x63A2,0x7708,
0x803D,0x8CAA,0x5854,0x642D,0x69BB,0x5B95,0x5E11,0x6E6F,
0xFA03,0x8569,0x514C,0x53F0,0x592A,0x6020,0x614B,0x6B86,
0x6C70,0x6CF0,0x7B1E,0x80CE,0x82D4,0x8DC6,0x90B0,0x98B1,
0xFA04,0x64C7,0x6FA4,0x6491,0x6504,0x514E,0x5410,0x571F,
0x8A0E,0x615F,0x6876,0xFA05,0x75DB,0x7B52,0x7D71,0x901A,
0x5806,0x69CC,0x817F,0x892A,0x9000,0x9839,0x5078,0x5957,
0x59AC,0x6295,0x900F,0x9B2A,0x615D,0x7279,0x95D6,0x5761,
0x5A46,0x5DF4,0x628A,0x64AD,0x64FA,0x6777,0x6CE2,0x6D3E,
0x722C,0x7436,0x7834,0x7F77,0x82AD,0x8DDB,0x9817,0x5224,
0x5742,0x677F,0x7248,0x74E3,0x8CA9,0x8FA6,0x9211,
       0x962A,0x516B,0x53ED,0x634C,0x4F69,0x5504,0x6096,
0x6557,0x6C9B,0x6D7F,0x724C,0x72FD,0x7A17,0x8987,0x8C9D,
0x5F6D,0x6F8E,0x70F9,0x81A8,0x610E,0x4FBF,0x504F,0x6241,
0x7247,0x7BC7,0x7DE8,0x7FE9,0x904D,0x97AD,0x9A19,0x8CB6,
0x576A,0x5E73,0x67B0,0x840D,0x8A55,0x5420,0x5B16,0x5E63,
0x5EE2,0x5F0A,0x6583,0x80BA,0x853D,0x9589,0x965B,0x4F48,
0x5305,0x530D,0x530F,0x5486,0x54FA,0x5703,0x5E03,0x6016,
0x629B,0x62B1,0x6355,0xFA06,0x6CE1,0x6D66,0x75B1,0x7832,
0x80DE,0x812F,0x82DE,0x8461,0x84B2,0x888D,0x8912,0x900B,
0x92EA,0x98FD,0x9B91,0x5E45,0x66B4,0x66DD,0x7011,0x7206,
0xFA07,0x4FF5,0x527D,0x5F6A,0x6153,0x6753,0x6A19,0x6F02,
0x74E2,0x7968,0x8868,0x8C79,0x98C7,0x98C4,0x9A43,
       0x54C1,0x7A1F,0x6953,0x8AF7,0x8C4A,0x98A8,0x99AE,
0x5F7C,0x62AB,0x75B2,0x76AE,0x88AB,0x907F,0x9642,0x5339,
0x5F3C,0x5FC5,0x6CCC,0x73CC,0x7562,0x758B,0x7B46,0x82FE,
0x999D,0x4E4F,0x903C,0x4E0B,0x4F55,0x53A6,0x590F,0x5EC8,
0x6630,0x6CB3,0x7455,0x8377,0x8766,0x8CC0,0x9050,0x971E,
0x9C15,0x58D1,0x5B78,0x8650,0x8B14,0x9DB4,0x5BD2,0x6068,
0x608D,0x65F1,0x6C57,0x6F22,0x6FA3,0x701A,0x7F55,0x7FF0,
0x9591,0x9592,0x9650,0x97D3,0x5272,0x8F44,0x51FD,0x542B,
0x54B8,0x5563,0x558A,0x6ABB,0x6DB5,0x7DD8,0x8266,0x929C,
0x9677,0x9E79,0x5408,0x54C8,0x76D2,0x86E4,0x95A4,0x95D4,
0x965C,0x4EA2,0x4F09,0x59EE,0x5AE6,0x5DF7,0x6052,0x6297,
0x676D,0x6841,0x6C86,0x6E2F,0x7F38,0x809B,0x822A,
       0xFA08,0xFA09,0x9805,0x4EA5,0x5055,0x54B3,0x5793,
0x595A,0x5B69,0x5BB3,0x61C8,0x6977,0x6D77,0x7023,0x87F9,
0x89E3,0x8A72,0x8AE7,0x9082,0x99ED,0x9AB8,0x52BE,0x6838,
0x5016,0x5E78,0x674F,0x8347,0x884C,0x4EAB,0x5411,0x56AE,
0x73E6,0x9115,0x97FF,0x9909,0x9957,0x9999,0x5653,0x589F,
0x865B,0x8A31,0x61B2,0x6AF6,0x737B,0x8ED2,0x6B47,0x96AA,
0x9A57,0x5955,0x7200,0x8D6B,0x9769,0x4FD4,0x5CF4,0x5F26,
0x61F8,0x665B,0x6CEB,0x70AB,0x7384,0x73B9,0x73FE,0x7729,
0x774D,0x7D43,0x7D62,0x7E23,0x8237,0x8852,0xFA0A,0x8CE2,
0x9249,0x986F,0x5B51,0x7A74,0x8840,0x9801,0x5ACC,0x4FE0,
0x5354,0x593E,0x5CFD,0x633E,0x6D79,0x72F9,0x8105,0x8107,
0x83A2,0x92CF,0x9830,0x4EA8,0x5144,0x5211,0x578B,
       0x5F62,0x6CC2,0x6ECE,0x7005,0x7050,0x70AF,0x7192,
0x73E9,0x7469,0x834A,0x87A2,0x8861,0x9008,0x90A2,0x93A3,
0x99A8,0x516E,0x5F57,0x60E0,0x6167,0x66B3,0x8559,0x8E4A,
0x91AF,0x978B,0x4E4E,0x4E92,0x547C,0x58D5,0x58FA,0x597D,
0x5CB5,0x5F27,0x6236,0x6248,0x660A,0x6667,0x6BEB,0x6D69,
0x6DCF,0x6E56,0x6EF8,0x6F94,0x6FE0,0x6FE9,0x705D,0x72D0,
0x7425,0x745A,0x74E0,0x7693,0x795C,0x7CCA,0x7E1E,0x80E1,
0x82A6,0x846B,0x84BF,0x864E,0x865F,0x8774,0x8B77,0x8C6A,
0x93AC,0x9800,0x9865,0x60D1,0x6216,0x9177,0x5A5A,0x660F,
0x6DF7,0x6E3E,0x743F,0x9B42,0x5FFD,0x60DA,0x7B0F,0x54C4,
0x5F18,0x6C5E,0x6CD3,0x6D2A,0x70D8,0x7D05,0x8679,0x8A0C,
0x9D3B,0x5316,0x548C,0x5B05,0x6A3A,0x706B,0x7575,
       0x798D,0x79BE,0x82B1,0x83EF,0x8A71,0x8B41,0x8CA8,
0x9774,0xFA0B,0x64F4,0x652B,0x78BA,0x78BB,0x7A6B,0x4E38,
0x559A,0x5950,0x5BA6,0x5E7B,0x60A3,0x63DB,0x6B61,0x6665,
0x6853,0x6E19,0x7165,0x74B0,0x7D08,0x9084,0x9A69,0x9C25,
0x6D3B,0x6ED1,0x733E,0x8C41,0x95CA,0x51F0,0x5E4C,0x5FA8,
0x604D,0x60F6,0x6130,0x614C,0x6643,0x6644,0x69A5,0x6CC1,
0x6E5F,0x6EC9,0x6F62,0x714C,0x749C,0x7687,0x7BC1,0x7C27,
0x8352,0x8757,0x9051,0x968D,0x9EC3,0x532F,0x56DE,0x5EFB,
0x5F8A,0x6062,0x6094,0x61F7,0x6666,0x6703,0x6A9C,0x6DEE,
0x6FAE,0x7070,0x736A,0x7E6A,0x81BE,0x8334,0x86D4,0x8AA8,
0x8CC4,0x5283,0x7372,0x5B96,0x6A6B,0x9404,0x54EE,0x5686,
0x5B5D,0x6548,0x6585,0x66C9,0x689F,0x6D8D,0x6DC6,
       0x723B,0x80B4,0x9175,0x9A4D,0x4FAF,0x5019,0x539A,
0x540E,0x543C,0x5589,0x55C5,0x5E3F,0x5F8C,0x673D,0x7166,
0x73DD,0x9005,0x52DB,0x52F3,0x5864,0x58CE,0x7104,0x718F,
0x71FB,0x85B0,0x8A13,0x6688,0x85A8,0x55A7,0x6684,0x714A,
0x8431,0x5349,0x5599,0x6BC1,0x5F59,0x5FBD,0x63EE,0x6689,
0x7147,0x8AF1,0x8F1D,0x9EBE,0x4F11,0x643A,0x70CB,0x7566,
0x8667,0x6064,0x8B4E,0x9DF8,0x5147,0x51F6,0x5308,0x6D36,
0x80F8,0x9ED1,0x6615,0x6B23,0x7098,0x75D5,0x5403,0x5C79,
0x7D07,0x8A16,0x6B20,0x6B3D,0x6B46,0x5438,0x6070,0x6D3D,
0x7FD5,0x8208,0x50D6,0x51DE,0x559C,0x566B,0x56CD,0x59EC,
0x5B09,0x5E0C,0x6199,0x6198,0x6231,0x665E,0x66E6,0x7199,
0x71B9,0x71BA,0x72A7,0x79A7,0x7A00,0x7FB2,0x8A70,
0/*  End of table; # of entries=8741(0x2225)+1 */

};
int ksc5601max=sizeof(tabksc5601)/sizeof(tabksc5601[0])-1;
        /* # of entries in the table. */

inline unsigned short convert_char_ksc5601_to_ucs2(unsigned char byte1, unsigned char byte2)
{
    int tab_idx = ((int)byte1 - 0x00a1) * 94 + (int)byte2 - 0x00a1;
    long code_ucs2;

    if (tab_idx >= 0 && tab_idx < ksc5601max) {
        code_ucs2 = tabksc5601[tab_idx];
        if (code_ucs2 != -1) return code_ucs2;
    }
 return 0;
}


Trackback Address :: http://joyholic.kr/trackback/260 관련글 쓰기
Tracked from 特殊網站設計 | 2012/05/11 00:17 | DEL
Joy Holic!! - KSC5601 <-> UNICODE 변환 코드 (테이블 이용)
Name
Password
Homepage
Secret
2008/02/26 10:16
브리슨헴 알고리즘(직선,원,타원) | 알고리즘 2004/10/26 18:51
http://blog.naver.com/jawung/6910456
직선 
브렌센헴 알고리즘의 가장 핵심이 되는 것은
에러텀이라는 것입니다. 오차이지요.
무한대의 해상도를 갖는 CRT는
존재하지 않으므로 CRT에 선을 그리려면
어쩔 수 없이 오차가 생기기 마련입니다.
이 오차를 이용하는 것입니다.

이 오차가 어느 일정 한도 이상이 되면
점을 찍는 위치를 증가시킵니다.
이해를 돕기위해 좌표계는 컴퓨터가 아니라
일반적인 스퀘어 좌표계를 쓰겠습니다.

     │
    3│            ???
    2│      ???
    1│???
     └───────────
    0  1 2 3 4 5 6 7 8 9
     
위의 그림은 (1,1)에서 (9,3)까지 선을 긋는
모습입니다. 당연히 매끈한 직선이 아니라
정수의 좌표를 갖는 점들의 집합이 되었죠.
설명을 돕기 위해 위의 좌표를 일반화하지
않았습니다. 그냥 가로가 x좌표고 세로가 y좌표
라고 해 두었습니다. 이것을 일반화시키면
설명이 복잡해지거든요.

자~ 이론 설명에 앞서, 선이 그려지는 과정부터
말씀드리죠.
우선 첫번째 점은 1,1에 찍습니다. 그 다음에
x좌표를 증가시킵니다. 그래서 에러텀을 살피고
만약 에러텀이 일정 한도 이하라면 그냥 넘어갑니다.
이렇게 진행하다가 보면 에러텀은 갈수록 커져가고,
나중에는 그 일정 한도라는 값을 넘어서게 됩니다.
그럼 y좌표를 증가시킵니다.
물론 y를 증가시켰으니 에러텀은 어느정도
줄어들겠죠. 에러텀에서 어느 값을 빼면 됩니다.
이 과정을 x좌표가 9가 될 때까지 반복하면 됩니다.
별로 어렵지 않게 이 방법이 실수의 가상좌표상의
점의 정수화와 거의 일치한다는 것을 증명할 수는
있지만 설명하기도 귀찮고, 또 설명한다고 해도
재미가 없어서 생략합니다. (흐-)

브렌센헴 알고리즘을 개략적으로 예를 들어 설명하자면,
그러니까... 무식한 예를 들죠.
만약 점이 0 아니면 1... 식의 정수값을 갖고,
그 증가율이 0.1이라면 어떻게 하겠습니까?
0.1, 0.2, 0.3, 0.4 까지는 그냥 0이라고 하고,
0.5부터 1이라고 하면 되겠죠?
그럼 이것을 아까의 과정으로 설명하죠.

우선 증가율이 0.1이므로...
처음 점은 0.
그 다음엔..
거기에 0.1을 더하면 0.1. 이것은 0.5보다 작으므로
오차는 0.1인채로 넘어가죠.
또 0.1을 더하면 0.2, 이것도 0.5보다 작으므로
오차는 0.2 ...
이런식으로 오차는 계속 늘어갑니다.
이러다가 0.5가 되는 순간 점의 값은 1로 뛰게 됩니다.
그럼 이제 오차는 -0.4가 되죠.
즉 에러에서 0.9를 뺀 것입니다.
이제 또 0.1을 더하면 -0.3으로 아직 0.5보다 작습니다.
또 더하고... 이렇게 하다가 보면 다시 에러텀은
0.5보다 커지고 점의 값은 또한번 증가하게 됩니다.

그렇다면 이것의 스케일을 좀 넓혀보죠.
왜냐하면 당연한 얘기지만 비록 곱셈 없이 덧셈뺄셈만
썼다지만 위의 것은 실수를 썼잖아요.
이것을 정수화 하기 위해선 약간의 연산을 해야 합니다.
그러기 위해선 아주 기초적인 미분식이 들어가는데...
생략하고 결론을 말하죠.
위의 것을 정수화 하고, 다음에 주어진 인수에 따라
재구성하면,

처음의 에러텀은 당연히 0이고,
증가율은 End_X - Start_X ... 즉 X의 변화량,
에러텀의 한계는 X의 변화량을 2로 나눈 값,
에러텀이 한계를 넘었을 때 빼주는 수는 Y의 변화량.

이렇게 됩니다.
그럼 방금 위의 인수를 바탕으로 알고리즘을 설명하면,

 "X의 값을 증가시킨 다음에 에러텀에 Delta_X를 더해준다.
 그 결과 에러텀이 Delta_X / 2 의 값보다 크거나 같으면
 Y의 값을 증가시키고 에러텀에선 Delta_Y를 빼준다.
 이 과정을 X가 선을 긋는 끝 좌표에 도달할 때까지 한다."
 
하나 주의해야 할 것은 위의 경우는 X의 기울기가 Y의
기울기보다 컸을 때의 얘기라는 것입니다.
만약 그 반대라면 진행 과정도 반대가 됩니다.

이해가 가시나요? 제딴엔 최대한 자세히 설명한 것입니다.
아래에 실제 소스를 싣죠.

void line2( int x1, int y1, int x2, int y2, char color )
{
  int x, y, temp;
  int delta_x, delta_y, half, error = 0;
  
  /* 한상 x2 >= x1, y2 > y1 이 되도록 한다. */
  if( x1 > x2 ){
    temp = x1;
    x1 = x2;
    x2 = temp;
   }
  if( y1 > y2 ){
    temp = y1;
    y1 = y2;
    y2 = temp;
   }
      
  /* x, y의 변화량?구한다. */
  delta_x = x2 - x1;
  delta_y = y2 - y1;
  
  /* 처음 시작점을 찍는다. */
  put_pixel( x1, y1, color );

  /* 각 좌표의 기울에 따라 선을 긋는다. */
  if( delta_x > delta_y ){
    y = y1;
    half = (int)( delta_x / 2 );

    for( x = x1+1; x <= x2; x ++ ){
      error += delta_y;
      if( error > half ){
        y ++;
        error -= delta_x;
       }
      put_pixel( x, y, color );
     }
   } else {
    x = x1;
    half = (int)( delta_y / 2 );

    for( y = y1+1; y <= y2; y ++ ){
      error += delta_x;
      if( error > half ){
        x ++;
        error -= delta_y;
       }
      put_pixel( x, y, color );
     }
   }
}

이해가 가셨나 모르겠군요.
위의 예제는 Game13h에서 직접 실행할 수 있도록
만든 것입니다. 함수명을 line2()로 한 것도
Game13h하고 충돌하지 말라고 한 것입니다.
하지만 점찍는 함수를 가진 어떠한 라이브러리에서도
사용하실 수 있습니다.
소스를 보시면 호출한 함수라곤 put_pixel()밖에
없다는 것을 알 수 있을 것입니다.
이 함수의 용도는 지정한 화면상의 위치에 정해진
색으로 점을 찍는 함수입니다.
그러니 이 함수만 만드신다면 어떤 스크린 모드에서도
사용하실 수 있습니다.
만약 기회가 난다면 브렌센헴 알고리즘을 보완한
알고리즘을 올리도록 하고, 지금은 이것으로 끝냅니다.
다음엔 원 그리는 부분을 올리도록 하겠습니다.


       
저번 강좌에 이어 이번엔 브렌센헴 알고리즘으로
원을 그리는 방법을 살펴 보겠습니다.
아시는 분은 다 아시다시피 이 브렌센헴 알고리즘은
구현이 쉽고 빠른 대신에 그 질이 좋지 못하다는
단점을 갖고 있습니다.
이것은 직선보다 원이 더 심합니다.
특히 타원의 경우엔 별도의 보완책 없이 쓰면
더욱 엉망이 되고 맙니다.
또한 여기선 화면의 종횡비는 계산에 넣지
않기로 하겠습니다.
모두 아시다시피 그래픽 모드마다 화면의 종횡비는
조금씩 다릅니다. 오직 몇가지 모드만이 정사각형
픽셀을 제공하는데, 모드 12h가 대표적인 예이죠.
그래픽 종횡비 보정을 해야 그래픽 모드에 상관없이
제대로 된 원을 그릴 수 있으나,
이번 강좌에선 그 부분을 빼기로 하겠습니다.
물론 그 부분이 어려워서는 아닙니다.
하지만 이 강좌는 브렌센헴 알고리즘만을 설명하기
위한 강좌이므로 주제에서 벋어난 부분의 설명은
가능한 한 줄일 생각입니다.

브렌센헴의 알고리즘은 직선이든 원이든 
그 전개 과정은 거의 흡사합니다.
어차피 에러텀에 일정 수를 더하고 그것이
일정 한도가 될 때까지 진행하다가 다음 순간
y의 값이 증가하고...
실제 그 과정에 대한 설명은 전 강좌를 봐 주시기
바랍니다.
그럼 직선과 원이 뭐가 달라지느냐...
이제 그것을 설명할까 합니다.

우선 원은 모든 방향이 대칭이 됩니다.
원을 그릴 때는 기본 4방향 대칭을 이용합니다.
그러나 이럴 경우엔 약간의 문제가 생깁니다.
처음 45도는 제대로 그려지다가 나중엔
X의 좌표 변화율에 비해 Y좌표 변화율이
너무 커져서 점이 떨어지게 됩니다.
그래서 45도씩 그려서 그것을 다시 대칭시킵니다.
이것은 원이 모든 방향으로 대칭된다는
성질로 쉽게 증명이 됩니다.
그러니 실제론 원이라기 보다 곡선으로 이루어진
8각형이라고 하는 것이 낫겠군요.

실제로 원을 그릴 때는 45도 각도만 그리고
그것을 8방향으로 대칭 시킵니다.
그러니 기울기에 따라 그리는 루틴이 갈릴
필요도 없겠죠.
무조건 증가 성분만 있는 부분을 그린 다음에
8방향으로 대칭 시키면 되니까요.
그럼에도 불구하고 원을 그리는 알고리즘은
직선에 비해 신경써줘야 할 부분이 좀 있습니다.

우선 대칭이 되는 네 점을 나열해 보겠습니다.
만약 원점이 (0,0)이라면 임의의 원상의 점 (x,y)는
각각 (y,x), (x,-y), (y,-x), (-x,y), (-y,x), (-x,-y),
(-y,-x)와 대칭됩니다.
만약 원점이 (x0, y0)라면, 이것은 알고리즘에 의해
구해진 (x,y)에 대해,
(x0+x, y0+y), (x0+x, y0-y), (x0+y, y0+x),
(x0+y, y0-x), (x0-x, y0+y), (x0-y, y0+x),
(x0-x, y0-y), (x0-y, y0-x) 의 팔방향으로
대칭된 점들이 구해집니다.

이제 원을 그리는 알고리즘을 위해 실제
원의 방정식에서 각 점의 위치를 구하는 식을
분석해 보기로 하겠습니다.
설명을 쉽게 하기 위해 원점을 (0,0)이라고
하겠습니다. 실제로 이렇게 구해진 x,y는
위의 방법을 사용해 실제 점으로 환원할 수
있습니다. 반지름은 ratio라고 합니다.

우선 원상의 점중에 1/8만을 구합니다.
어느 방향을 하든 상관없지만 이왕이면
x는 증가, y는 감소하는 원의 우상단 부분을
사용합니다. 그럼 x는 0부터 시작해서 y까지
한다고 하면 됩니다.
왜냐하면 x와 y의 반지름에 관한 관계는
서로 동등하므로 만약 이 두값이 같다면 1/8의
지점에 도달한 것이기 때문입니다.
(이런 것까지 증명해줄 필요는 없겠죠?)
물론 y값도 상수가 아닙니다. 그러니 루프의
횟수는 처음부터 정해진 것이 아닙니다.
루프가 진행되어 감에 따라 일정한 규칙에
따라 y좌표도 감소하므로 대체로 처음 정한
y값보다 작은 횟수로 루프가 끝나기 마련입니다.

출발좌표가 (0, ratio)이므로 다음 점의 좌표는
(1, ratio) 이든가 (1, ratio-1)이 될 것입니다.
이 둘중에 어느것인지 판단하는 것은 전 강좌의
직선의 에러텀을 연상하십시요.
다음 점을 구하면 이것이 무엇이든간에 이론적인
원의 점에서 보면 오차가 있습니다.
이 오차의 절대값을 d라고 놓으면,

         d = |(x² + y²) - ratio²|

라는 식이 성립합니다.
이제 위에서 얘기한 (1, ratio)와 (1, ratio-1)
를 사용해 오차를 각각 구할 수 있습니다.
그럼 이 오차가 작은 경우가 우리가 구하고자
하는 점이겠죠?
편이상 (1, ratio)의 오차를 d(S₁),
(1, ratio-1)의 오차를 d(D₁)라고 놓겠습니다.
그럼 d(D₁) - d(S₁)의 값이 0보다 작으면
점이 감소한 것이고, 그렇지 않으면 점은 감소하지
않은 것입니다. 점이 감소했다면 그 오차에서
일정 값을 빼 줍니다. 이것은 전 강좌에서도
언급한 적이 있으니 설명은 생략합니다.
이런 과정을 x를 증가시켜가며 진행해서 원의
1/8의 정도를 그릴 때까지 반복합니다.
x가 언제 1/8지점까지 왔는가는 곧 설명합니다.

이정도 했으면 원 그리기 알고리즘의 대략적인
설명이 되었다고 생각합니다.
브렌센헴 알고리즘을 설명하기 위해 약간
바람을 잡은 셈이죠.
이제부터가 진짜 브렌센헴 알고리즘입니다.
위의 대략적인 이해가 끝났다면 스스로
브렌센헴 알고리즘의 구조를 머릿속에
떠올릴 수 있을 것입니다.
수학이 골치아프시다는 분은 그냥 아래 설명과
소스를 보십시요.
저도 수학과지만 수학은 무지 골치아픕니다.

원의 방정식에서 브렌센헴 알고리즘을
이끌어 내는 과정은 생략합니다.
솔직히 말하면 저도 하라면 헛갈려서
못해요. 유도식을 보고서야 할 수 있죠.
흐흐- 이해는 하지만 하라면 못하는 것...
이것이 우리나라 교육의 문제랄까?
얘기가 잠깐 옆으로 샜는데...
간단히 말씀드리죠.

우선 초기값으로 x, y, thres를 설정합니다.
여기서 thres는 에러텀 비슷한 것인데
아주 약간 다릅니다. 어떻게 다른가는
설명이 애매하니 생략합니다.(생략 투성이군...)
x의 초기값은 0, y의 초기값은 반지름, thres는
3에서 지름을 뺀 값으로 합니다.
그리고 루프를 시작하는데...
그 종결조건은 x가 y보다 커지는 경우입니다.
만약 thres가 0보다 작으면 thres를
6 + x * 4만큼 증가시킵니다. 그렇지 않다면
thres는 10 + (x - y) * 4만큼 증가시키고
y를 하나 감소시킵니다.
이렇게 하면 x와 y의 값이 모두 구해지므로
원점을 중심으로 8방향으로 점을 모두 찍습니다.
여기까지가 루프이며, 이제 x를 증가시키면 되죠.

하나 예외적인 상황이 있는데,
반지름이 0인 경우죠. 이럴 경우엔 점 하나만을 찍은
채 함수를 종결합니다.
자, 아래 소스가 있습니다.

void circle2( int x0, int y0, word ratio, byte color )
{
   int x, y, thres;

   if( ratio == 0 ){
     put_pixel( x0, y0, color );
     return;
    }

   y = ratio;
   thres = 3 - (ratio + ratio);

   for( x = 0; x < y; x ++ ){
     if( thres < 0 )
       thres += 6 + (x << 2);
      else{
       thres += 10 + ((x - y) << 2);
       y --;
      }

     put_pixel( x0+x, y0+y, color ); put_pixel( x0+y, y0+x, color );
     put_pixel( x0-x, y0+y, color ); put_pixel( x0-y, y0+x, color );
     put_pixel( x0+x, y0-y, color ); put_pixel( x0+y, y0-x, color );
     put_pixel( x0-x, y0-y, color ); put_pixel( x0-y, y0-x, color );
    }
}

전번 강좌와 마찬가지로 점찍는 함수를 갖춘 다른 모든
프로그램에서 사용이 가능합니다.
이번에도 어제처럼 날림으로 강좌를 끝냈는데,
이유는(핑계는) 전산학도나 수학도를 위한 공식 유도가
아니라 브렌센헴 알고리즘이 이렇다 싶은 정도만
얘기하고 실제로 컴퓨터로 옮기는 것만을 설명하는 것이
이 강좌의 목적이기 때문입니다.
아마 다음 강좌인 타원 그리기 알고리즘은 더욱 날림이
될 것입니다. 원 그리기보다 배는 복잡하니까요.
그리고 제가 브렌센헴 알고리즘을 공부하기 시작한 것도
그렇게 오래되지 않았으니 강좌에서 틀린 부분도 있을
것입니다. 만약 그렇다면 아주 마음 놓고 씹어주세요.
언제든 칼맞을 준비가 되어 있습니다. (헤-)
그럼 조금이라도 도움이 되었기를...

타원
안녕하세요?
제가 개강 때문에 좀 바빠서 강좌가
늦어졌습니다. 원래 계획으론 하루에
하나씩 3일에 다 끝낼려고 했는데...
이번엔 브리슨헴 알고리즘 강좌
마지막인 타원 그리기입니다.
하나 미리 얘기해 둘 것은,
비록 여기에 브렌센헴 알고리즘의
강좌라지만 이 타원 그리기 알고리즘은
브렌센헴 알고리즘이 아닙니다.
실제 그 구현을 보면 브렌센헴 알고리즘과
유사하지만 어느 참고서적에서도 이것을
브렌센헴 알고리즘이라고 하지 않습니다.
하지만 연관관계도 있고, 그 구현면에서도
유사하니까 이 강좌에선 같은 계열에서
취급하는 것입니다.
이점 착오 없으시기 바랍니다.

척 보기에도 알 수 있듯이, 타원의 방정식은
원의 방정식보다 어렵습니다.
그리고 속도도 더 늦고요.
하지만 약간 변형을 하면 상당히 빠른 속도를
낼 수도 있습니다. 이 알고리즘의 이름은
모르겠지만 구현 방법면에선 할 하드베르그의
알고리즘과 유사하므로 그쪽 계통이 아닌가
생각됩니다. 속도는 무척 빠르지만
여기서는 다루지 않겠습니다.
만약 얘기를 하자면 원그리기까지
또다시 거슬러 올라가야 하기 때문입니다.

타원은 원의 경우와는 달리 4방향이 대칭됩니다.
그리고 단순 증가방식만을 쓰지 않습니다.
타원의 경우엔 선그리기처럼 45도를
기점으로 그 기울기(?)가 다르므로 구현할 때도
이 두경우를 나누어서 해 주어야 합니다.
그러니 실제론 4분의 1만을 그리고 나머지는
대칭으로 찍으면 되고, 또 이 1/4도 두가지로
나누어서 그려야 합니다.

이 알고리즘을 위한 설명은 따로 존재하지 않고
앞에서 한 설명을 바탕으로 유도식만을
보입니다. 실력이 있는 분들은 이해하실 수 있을
것입니다. 만약 이해가 안된다면 다른 참고서적들을
참고하세요. 아마 많은 그래픽 서적에서 이것을
다루고 있을 것입니다.

유도식이 긴 관계로 초기항과 결과만을 쓰기로
하겠습니다.
우선 타운의 방정식이

  (x0 - x)?  (y0 - y)?
  ────  - ────  = 1
     a?         b?

이라는 것은 후진 중학교 안나온 이상 다 아는
사실. 이것을 약간 변형시켜서,

  b?x? + a?y?- a?b? = 0
  
이라고 놓고 이것을 앞의 방법을 통해 d를 구하면

  d = b?(x+1)?+a?(y-1/2)?-a?b?
  
이고, 각각 d₁,d₂, d₃... 에 대해 
d₂-d₁, d₃- d₂... 등을 구하면

 d₂- d₁= -2a²y₁
 d₃- d₂= -2a²y₂
 .
 .
 .
 
이다. 이것은 기울기, 즉 dy/dx가 -1 이상일
때이고, 만약 그 이하라면

 d₂- d₁= -2a²y₁+ a²
 .
 .
 
으로 바뀝니다.
또한 생각해 줘야 할 것이 한두개 더 있지만
설명을 생략하기로 합니다.

이렇게 생각해 줘야 할 경우가 두가지인데다가
그 성격이 다르기 때문에 타원을 그리는 데엔
크게 나누어 두개의 다른 루프를 돌려야 합니다.
게다가 루프 내에서 당연히 판단 루프가
들어가죠. 총 4가지의 다른 상황을 처리하는
함수로 타원 그리는 함수는 구성되어 있습니다.

대략적으로 설명한대다가 알고리즘을 구현하기
위해 충분한 자료를 다 제시하지 못했습니다.
이유는 앞서 말했듯이 알고리즘 자체에
대한 강좌라기 보다는 소개와 실제 그 소스를
공개하는 데에 그 목적을 두기 때문입니다.

void ellipse2( int x0, int y0, word a0, word b0, byte color )
{
  int x = 0, y = b0;
  long a = a0, b = b0;
  long a_squ = a * a;
  long two_a_squ = a_squ << 1;
  long b_squ = b * b;
  long two_b_squ = b_squ << 1;
  long d, dx, dy;

  d = b_squ - a_squ*b + (a_squ >> 2);
  dx = 0;
  dy = two_a_squ * b;

  while( dx < dy ){
    put_pixel( x0+x, y0+y, color );
    put_pixel( x0-x, y0+y, color );
    put_pixel( x0+x, y0-y, color );
    put_pixel( x0-x, y0-y, color );
    if( d > 0 ){
      y --;
      dy -= two_a_squ;
      d -= dy;
     }
    x ++;
    dx += two_b_squ;
    d += b_squ + dx;
   }

  d += ( 3*(a_squ - b_squ)/2 - (dx+dy)/2 );

  while( y >= 0 ){
    put_pixel( x0+x, y0+y, color );
    put_pixel( x0-x, y0+y, color );
    put_pixel( x0+x, y0-y, color );
    put_pixel( x0-x, y0-y, color );
    if( d < 0 ){
      x ++;
      dx += two_b_squ;
      d += dx;
     }
    y --;
    dy -= two_a_squ;
    d += a_squ - dy;
   }
}

앞의 두 강좌와 마찬가지로 점찍는 기능을 가진 다른
모든 프로그램에서 실행이 됩니다.
아마 설명이 그다지 잘 되지 못했을테고,
게다가 알고리즘 구현에 필요한 것을 전부
설명하지 못했기 때문에 설명 자체만으로 프로그램을
짤 수는 없을 것입니다. 설명이 미흡하다고 생각하면
다른 참고서적을 살피고, 실력이 있으시다면
소스와 앞에서 설명한 몇가지 자료만으로 직접 분석해
보시기 바랍니다.

미흡한 강좌, 끝까지 봐 주신 여러분께 감사드립니다.
한번 그래픽 이론을 알고 싶다고 생각하고 보신
분들은 실망이 크시겠지만 그냥 한번 어떤 것인가
맛만 보겠다고 생각하시고 강좌를 보신 분들은
약간 도음이 되셨을 것입니다.

요즘 게임들이 테크닉 위주로 많이 나가는데,
이제는 테크닉보다 탄탄한 자료 구조와 이론을
바탕으로 그곳의 위에 화려한 테크닉의 옷을
입힌 알찬 게임이 뮌?나왔으면 좋겠습니다.
특히 3차원 게임에 관한 관심이 높아지는데
조금 아는 듯 싶다 하는 분들이 자신의 소스를
공개하길 꺼리는 것 같군요.
저도 좀 많이 배우고 싶은데...
아무쪼록 혹시나 3차원 그래픽 쪽으로 지식이
조금 있는 분께서 이 글을 읽으신다면
우리나라 게임의 발전을 위해서도
소스를 팍팍 공개하시고, 강좌도 하시면서
자신의 지식을 아낌없이 나누어 주시길 바랍니다.
그럼...
Trackback Address :: http://joyholic.kr/trackback/258 관련글 쓰기
Name
Password
Homepage
Secret
2008/02/21 13:14
윈도우2000/XP 이상에는 COLOR 라는 명령이 있더군요. 명령 프롬프트에 출력되는 글자색을 바꾸는 명령입니다:

D:\Z>color /?
콘솔의 기본 문자색과 배경색을 설정합니다.

COLOR [attr]

   attr        콘솔로 출력되는 색 속성을 지정합니다.

색 속성은 두 자리의 16진수로 지정됩니다. 즉, 첫째 자리는 배경색에 해당되고
둘째 자리는 문자색에 해당됩니다.  각 자리 수는 다음 값이 될 수 있습니다.

   0 = 검정색       8 = 회색
   1 = 파랑색       9 = 연한 파랑색
   2 = 초록색       A = 연한 초록색
   3 = 옥색         B = 연한 옥색
   4 = 빨강색       C = 연한 빨강색
   5 = 자주색       D = 연한 자주색
   6 = 노랑색       E = 연한 노랑색
   7 = 흰색         F = 밝은 흰색

인수가 주어지지 않으면 이 명령은 CMD.EXE가 시작할 때 사용한
색으로 복원됩니다. 값은 현재 콘솔 창에서 가져 오거나, /T 명령 중
스위치나 DefaultColor 레지스트리 값으로부터 가져 옵니다.

COLOR 명령은 문자색과 배경색을 같게 지정하여 실행하려고 할 경우
ERRORLEVEL을 1로 설정합니다.

예를 들면 "COLOR fc"는 밝은 흰색 위에 연한 빨강색의 문자색을 나타냅니다.

D:\Z>


그런데 COLOR 명령을 주면, 이미 화면에 출력되어 있던 글자들의 색이, 일제히 같은 색으로 변경되어 버리는 문제가 있었습니다.

제가 원하던 것은 특정 문자열에만 색을 주는 것이었는데, COLOR 명령은, 명령 프롬프트 화면 자체의 색을 전부 바꾸어 버리는 것이었습니다.


그래서 특정 문자열의 색만 바꾸는 유틸리티를 하나 만들었습니다. 원래는 Perl(펄)로 만들었는데 속도가 좀 느려서, C로 만들었더니 아주 빠르더군요.




다음의 코드를 setclr.cpp 라는 이름으로 저장한 후 비주얼C 컴파일러로 컴파일하면 setclr.exe 라는 실행파일이 만들어집니다.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>

#define THIS_PROGRAM_VERSION "1.0"
char this_program_name[] = "SETCLR";

void setrgb(char *s);
int help(void);



int main(int argc, char *argv[]) {

   if (argc == 1) help();
   setrgb(argv[1]);

   return 0;
}




void setrgb(char *s) {
   WORD attr, previous_background_color;
   char fc, bc;
   HANDLE hConsole = GetStdHandle (STD_OUTPUT_HANDLE); // GetConsoleScreenBufferInfo에 필요
   CONSOLE_SCREEN_BUFFER_INFO csbi;                    // GetConsoleScreenBufferInfo에 필요

   GetConsoleScreenBufferInfo(hConsole, &csbi);
   // 전경색과 배경색이 혼합된 wAttributes에서 배경색만 추출
   // 11111000 등에서 앞의 4비트가 배경색이므로 4비트를 우측으로 이동시켰다가 원위치시키면
   // 뒤의 네자리 즉 전경색은 0000로 채워져 배경색만 남음.
   previous_background_color = csbi.wAttributes >> 0x4;
   previous_background_color = previous_background_color << 0x4;



   strupr(s);
   // 만약 두 색이 모두 지정되어 있으면 처음 숫자는 배경색, 다음 숫자는 전경색.
   // 한 색만 지정돼 있으면 전경색
   // 이것은 MS의 color 명령의 옵션 순서임
   // color 명령은, 한 색만 지정하면 전경색. 두 색을 지정하면 앞색을 배경색으로 인식해 버림.
   if (s[1] != 0) { // 만약 두 색이 모두 지정되어 있으면
      bc = s[0]; fc = s[1];
   } else {         // 한 색만 지정하면
      fc = s[0]; bc = 'X';
   }




   /* Foreground Color */
   switch (fc) {
      case '0': // Black
         attr = 0;
         break;
      case '1': // Blue
         attr = FOREGROUND_BLUE;
         break;
      case '2': // Green
         attr = FOREGROUND_GREEN;
         break;
      case '3': // Aqua (Cyan)
         attr = FOREGROUND_GREEN | FOREGROUND_BLUE;
         break;
      case '4': // Red
         attr = FOREGROUND_RED;
         break;
      case '5': // Purple (Magenta)
         attr = FOREGROUND_RED | FOREGROUND_BLUE;
         break;
      case '6': // Yellow
         attr = FOREGROUND_RED | FOREGROUND_GREEN;
         break;
      case '7': // White (Gray: System Default)
         attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
         break;
      case '8': // Gray (Light Black)
         attr = FOREGROUND_INTENSITY;
         break;
      case '9': // Light Blue
         attr = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
         break;
      case 'A': // Light Green
         attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
         break;
      case 'B': // Light Aqua (Cyan)
         attr = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
         break;
      case 'C': // Light Red
         attr = FOREGROUND_RED | FOREGROUND_INTENSITY;
         break;
      case 'D': // Light Purple (Magenta)
         attr = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
         break;
      case 'E': // Light Yellow
         attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
         break;
      case 'F': // Bright White
         attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
         break;
      default : help();
         break;
   }




   /* Background Color */
   switch (bc) {
      case 'X': // 배경색이 지정돼 있지 않으면 기존의 배경색을 사용.
                // 전경색만 지정하면 배경색이 항상 검정으로 되어 버림.
         attr = attr | previous_background_color;
         break;
      case '0': // Black
         attr = attr | 0;
         break;
      case '1': // Blue
         attr = attr | BACKGROUND_BLUE;
         break;
      case '2': // Green
         attr = attr | BACKGROUND_GREEN;
         break;
      case '3': // Aqua (Cyan)
         attr = attr | BACKGROUND_GREEN | BACKGROUND_BLUE;
         break;
      case '4': // Red
         attr = attr | BACKGROUND_RED;
         break;
      case '5': // Purple (Magenta)
         attr = attr | BACKGROUND_RED | BACKGROUND_BLUE;
         break;
      case '6': // Yellow
         attr = attr | BACKGROUND_RED | BACKGROUND_GREEN;
         break;
      case '7': // White (Gray: System Default)
         attr = attr | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
         break;
      case '8': // Gray (Light Black)
         attr = attr | BACKGROUND_INTENSITY;
         break;
      case '9': // Light Blue
         attr = attr | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
         break;
      case 'A': // Light Green
         attr = attr | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
         break;
      case 'B': // Light Aqua (Cyan)
         attr = attr | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
         break;
      case 'C': // Light Red
         attr = attr | BACKGROUND_RED | BACKGROUND_INTENSITY;
         break;
      case 'D': // Light Purple (Magenta)
         attr = attr | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
         break;
      case 'E': // Light Yellow
         attr = attr | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY;
         break;
      case 'F': // Bright White
         attr = attr | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
         break;
      default : help();
         break;
   }

   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attr);
}




int help(void) {
   printf("\n"
      "%s " THIS_PROGRAM_VERSION "\n"
      "(c) 2006 mwultong.blogspot.com\n"
      "\n"
      "Set Console Letter Color (Microsoft COLOR command compatible)\n"
      "\n\n"
      "Usage   : %s < Foreground Color >\n"
      "          %s < Background Color + Foreground Color >\n\n"
      "Example : %s f  :: Bright White\n"
      "          %s 1e :: Bright Yellow on Blue\n"
      "          %s 07 :: White on Black (System Default)\n"
      "\n\n"
      "\t0 = Black (Invisible)               8 = Gray (Dark Gray)\n"
      "\t1 = Blue                            9 = Light Blue\n"
      "\t2 = Green                           A = Light Green\n"
      "\t3 = Aqua (Cyan)                     B = Light Aqua (Light Cyan)\n"
      "\t4 = Red                             C = Light Red\n"
      "\t5 = Purple (Magenta)                D = Light Purple (Light Magenta)\n"
      "\t6 = Yellow                          E = Light Yellow\n"
      "\t7 = White (Gray: System Default)    F = Bright White\n"
      "\n"
      , this_program_name,
        this_program_name, this_program_name,
        this_program_name, this_program_name, this_program_name
      );

   exit(EXIT_FAILURE);
}



옵션 없이 setclr.exe 를 실행하면 도움말이 나옵니다.

setclr f
라고 하면, 그 다음부터 출력되는 글자들은 모두 하얗게 표시됩니다.

setclr c
라고 하면, 그 다음부터 출력되는 글자들은 모두 빨갛게 표시됩니다.

setclr 7
이라고 하면 원래의 색 즉 밝은 회색으로 글자들이 출력됩니다.




물론 이 유틸리티는 그냥 쓰는 것보다는, 배치 파일 속에서 쓰는 것이 적합합니다. 다음과 같이 명령행에서도 배치 파일 비슷하게 실행시킬 수 있습니다.

setclr c & echo Red & setclr f & echo White & setclr 7 & echo Default

명령 프롬프트에서 위와 같은 명령을 준다면

Red
White
Default

가 각각 빨강, 흰색, 회색으로 글자가 나올 것입니다.




추가 사항:
Windows.h 라는 헤더파일이 필요합니다. 참고 : [C언어/C++] 비주얼C로 콘솔 글자 색깔 변경, 볼랜드C++의 textcolor() 함수 구현
Trackback Address :: http://joyholic.kr/trackback/256 관련글 쓰기
Name
Password
Homepage
Secret
2008/02/15 14:31

C:\>subst /?
Associates a path with a drive letter.

SUBST [drive1: [drive2:]path]
SUBST drive1: /D

  drive1:        Specifies a virtual drive to which you want to assign a path.
  [drive2:]path  Specifies a physical drive and path you want to assign to
                 a virtual drive.
  /D             Deletes a substituted (virtual) drive.

Type SUBST with no parameters to display a list of current virtual drives.



C:\>subst e: C:\(temp)

C:\>subst
E:\: => C:\(temp)

C:\>subst e: /d

C:\>subst

Trackback Address :: http://joyholic.kr/trackback/249 관련글 쓰기
Name
Password
Homepage
Secret
2008/02/14 11:51

7Zip: http://www.7-zip.org/ 

KZip: http://advsys.net/ken/utils.htm

Trackback Address :: http://joyholic.kr/trackback/245 관련글 쓰기
Name
Password
Homepage
Secret
2008/02/14 11:49
For compressing PNG Files

http://advancemame.sourceforge.net/comp-readme.html
: Official Web site

https://forum.gameloft.org/viewtopic.php?p=2778 : Gameloft Forum

http://advsys.net/ken/utils.htm : Official Web site

Trackback Address :: http://joyholic.kr/trackback/244 관련글 쓰기
Name
Password
Homepage
Secret
2008/01/31 12:43

글꼴이 하도 나를 성가시게 해서, 한번 검색해 봤다.

http://www.microsoft.com/downloads/details.aspx?familyid=22e69ae4-7e40-4807-8a86-b3d36fab68d3&displaylang=en

비주얼 스튜디오 2005용 글꼴..

Trackback Address :: http://joyholic.kr/trackback/235 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/21 18:05

I find out the diffs from the game 3DNeedForDrift, it used Timers to call Canvas.repaint().

It causes an Nullexception in Timer.run()->mainloop()->canvas.repaint(), there is one frame with NULL canvas.

Let’s see the differences of Timer’s implement:

[J2ME/MIDP2.0]

class TimerThread extends Thread {
…….
public void run() {
        try {
            mainLoop();
        } catch (Throwable t) {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized (queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

private void mainLoop() {
        while (true) {
            try {
                       ……..      
               if (taskFired) { // Task fired; run it, holding no locks
               try {
                       task.run();
                    } catch (Exception e) {
                       // Cancel tasks that cause exceptions
                       task.cancel();
                   }

              }
            } catch (InterruptedException e) {
            }
        }
    }
}

[J2SE/1.4.2]

class TimerThread extends Thread {
…….
public void run() {
        try {
            mainLoop();
        } finally {
            // Somone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

private void mainLoop() {
        while (true) {
            try {
                       ……..      
               if (taskFired)  // Task fired; run it, holding no locks
                    task.run();

            } catch (InterruptedException e) {
            }
        }
    }
}

===================

In J2ME, the try…catch… block makes the Timer running normally even if the task throws an exception.

But in J2SE, there is none protection, it’s why the 3DNeedForDrift failed running in KEmulator!

crack rt.jar to run the game:

Trackback Address :: http://joyholic.kr/trackback/176 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/13 13:54

Introduction

 

Code performance is vital to a successful game title, and poor performance can lead to shipping delays and cutting of content as well as a much more expensive game development process. Getting code generation right has to start from the beginning of a project, as the architecture of a game system determines the performance as much as anything else. A naïve approach to architecture design will spell disaster for a project.

 

Things are only getting more complicated, since next-gen consoles have introduced us to multiple cores, longer pipelines, Level 2 caches, and multi-thread issues. Most next-gen consoles have chosen to pack more computing elements onto the chips than most workstations. This comes at the price of the omission of instruction renaming and large caches intended to make legacy applications run faster.

 

The plus side is that next-gen consoles are capable of hundreds of gigaflops, or about a thousand Cray-1s, the most powerful supercomputer available in 1976 when I was first learning to code. This article illustrates a few of the key elements that lead to high performance code in the system as a whole.

 

 

Memory Access

 

The most important optimization you can make is to reduce the size of your data. A typical title will spend a significant period of its time waiting for Level 1 and Level 2 cache misses. Level 1 misses are bad, Level 2 misses can be catastrophic.

 

A typical Level 1 cache miss on a modern processor will waste tens of cycles, whereas a Level 2 miss will waste hundreds. It is very important, therefore, that the game state is kept to under the size of the Level 2 cache, otherwise the cache will thrash on every frame and performance will plummet. The drop in performance will be sudden and unexplained, but the cause will be that the code is touching too much data in a frame. If the code touches 511k of Level 2 cache in a frame on a machine that has 512k, it will run fine. As soon as it increases to more than 512k, the LRU mechanism will cause every access to fail. This was not the case on previous generation consoles that had only Level 1 caches with low cache miss penalties.

 

Use of bit fields, carefully designed access methods and heavy duty pre-processing of game data will significantly reduce game state size. Memory with larger scope, like collision plane information or AI route finding should be encouraged to access the same patches of memory on a frame-by-frame basis. Even though the datasets may be quite large, regular access will not thrash the cache.

 

Avoid using look-up tables and constants in memory. These will cause cache misses and will thrash Level 2 cache access. GPUs suffer from memory access problems also. Typically a GPU will also have a cache that follows the same rules. Use of bytes and shorts for vertex data is recommended and balancing texture LOD by adjusting mip-map bias is an important tool. Use anisotropic filtering to sharpen textures instead of positive LOD bias.

 

Most advanced game systems have a method of sub-sampling the artist's original textures to reduce the texture usage. Different scenes have different use of the same texture, so that the texture may be loaded at a lower resolution if the player is never going to encounter that texture close-up. Finally, the smaller the texture pool size, the higher will be the GPU performance. Sorting the scene by texture will preserve GPU cache coherency.

 

 

Branch Avoidance

 

The next biggest source of optimization is to re-code critical methods to avoid branches. Included in this are virtual function calls and pointers-to-functions. Branch miss-prediction causes pipeline stalls and instruction look-ahead failure, which is usually much more expensive. Templates are great ways to generate variations on methods that can avoid virtual function calls and often function pointers passed as constants to inline functions can be converted to direct calls. Profile-driven branch tuning can feed-back information about branches to an executable. Branches usually have two forms, unlikely and likely. When a branch is first encountered by the thread, the branch direction is hinted by the likelihood encoded in the instruction. A profile-driven optimizer can choose branch likelihood and re-order blocks of code to avoid loading uncommon blocks into Level 1 cache.

 

We can eliminate branches entirely by changing code of this kind:

 

if( x == 0 ) y = z;

to

y = x == 0 ? z : y;

and

if( ptr != 0 ) ptr->next = prev;

to

*( ptr != 0 ? &ptr->next : &dummy ) = prev;

 

With a good compiler, this will execute far faster than the code with branches that the “if” form will generate. Next-gen consoles have deep pipelines that require large uninterrupted function bodies to be able to schedule efficiently.

 

 

Inlining


After branch avoidance, inlining can be used to remove call overhead and increase function body size, but at the expense of extra Level 2 cache usage. Some compilers, like the SN Systems compiler (SNC), will auto-inline functions if requested. Even legacy code can be made to go faster provided the scope of functions is limited by “static.”

 

Given a fresh design, it is possible to arrange for all functions to be inlinable, especially if all functions are defined in header files and the large parts of the program are compiled as a single unit. Code like this will actually compile and link faster than its multi-module counterpart owing to the reduction of debug information emitted by the compiler.

 

Most compilers have n-squared dependencies on optimization, so if compile times get high, some bigger blocks may need to be split or lower optimization levels may have to be used. Another benefit of inlining is in the field of alias analysis which will help the compiler to remove unnecessary loads and stores and to lengthen the blocks available to the scheduler. Constraints on register usage are eliminated by in-lining.

 


Floating Point Pipeline Usage

 

The current generation of processors have long floating point pipelines that may allow out-of-order completion. This allows faster clock rates because the number of gates that the pipeline travels through can be reduced if an operation is spread over several cycles. The price that we pay for this is that the results of floating point operations are not available for tens of cycles. It is important therefore to avoid using floating point results for as long as possible and to make function bodies that use floating point code as large as possible with no branches.

 

In particular, be careful when converting the result of a floating point operation to an integer. The pipeline will have to be flushed, causing big delays.

 

 

Vector Units and Supervectors

 

Ideally, all maths must be done using the vector units available either on main processors or on coprocessors. Unfortunately, retro-fitting a vector math library to float-based code is rarely effective, resulting in only a few percent improvement to critical functions. A scalar type should be included in a vector math library and used extensively in the place of “float” arithmetic in cases where the cost of copying from scalar to vector is prohibitive. One way of getting good use out of your vector units is to use the concept of Supervectors. Instead of vectors of four floats, operate on units of sixteen or more to absorb the latency between instructions.

 

Instead of:

 

void Add( vector &elem, vector a, vector b )
{
  elem = vec_add( a, b );
}

 

Write:

 

void Add( vector elem[], vector a[], vector b[] )
{
  elem[ 0 ] = vec_add( a[ 0 ], b[ 0 ] );
  elem[ 1 ] = vec_add( a[ 1 ], b[ 1 ] );
  elem[ 2 ] = vec_add( a[ 2 ], b[ 2 ] );
  elem[ 3 ] = vec_add( a[ 3 ], b[ 3 ] );
  elem[ 4 ] = vec_add( a[ 4 ], b[ 4 ] );
  elem[ 5 ] = vec_add( a[ 5 ], b[ 5 ] );
  elem[ 6 ] = vec_add( a[ 6 ], b[ 6 ] );
  elem[ 7 ] = vec_add( a[ 7 ], b[ 7 ] );
}

 

The second function will take about the same time to complete as the first, because of FPU latency, but will do eight times the work. Note that use of the __restrict keyword may be necessary if the function is not inlined to help alias analysis. This is an important numeric optimization. The general rule of thumb is to do the same thing multiple times. Pipelines work best when the same input instruction is used during the lifetime of the pipeline operation.

 

Next-gen coprocessors have long latencies, but large numbers of available registers which can be exploited to fill the gaps caused by read-after-write latency issues. Structures of arrays methods can be used to apply regular arithmetic to vector units where Supervectors are treated like scalars. With SOA methods, values for many objects are represented as arrays with the x, y and z components of a vector, for example, all being elements in three separate Supervectors. It is important to batch up numerical operations so that they can be performed together. For example, if we have a particle system that adds the gravity value to the velocity, the gravity value needs only to be loaded once if all the particles of the same type are evolved together in one loop.

 

If spare GPU power is available, simple linear tasks can be run on the GPU also, although unless the result is directly used for rendering alone, synchronization can be an issue. Again it is essential to batch up tasks that are similar.

 

 

Thread Management

 

First-timers to embedded multithread environments will usually make the mistake of spawning too many threads. It is important to spawn only as many threads as there are hardware resources to execute them, otherwise the operating system will thrash the threads and take up a considerable overhead. Although multiple threads can be created on a single core, they will often fight for resources, negating the advantage of having them. Care should be taken to schedule complimentary tasks into threads. For example, mix a memory-intensive task with a computation intensive task, so that they do not compete for memory resources. Spawn the threads in the game initialization code; it is not wise to attempt to spawn threads in the game loop as this is an expensive operation. Profiling the game code early in the development cycle can spot operating system overheads before they become serious.

 

Next-gen engines may have an Execution Plan where tasks are given fixed timeslots to complete in with the timeslots organized so that the outputs of one task feed the inputs of another. Failure of a task to complete in time may cause an assertion failure in pre-production builds with the short-term irritation tolerated to prevent last-minute optimization panics and slipped deadlines. Execution plans can be represented as XML files and tie nicely into systems like the Collada scene description language. Careful design of the execution plan for a scene or sub-scene is essential. If the resources available in a scene are finite, then it is possible to guarantee frame rate without over-allocation.

 

Using multiple threads on one core reduces effective latencies as instructions are executed alternately. A good compiler will have an optimization option to support this.

 

 

Serialization and Pre-compilation

 

Serialization in-game, which causes the long load times common on PC games, can be avoided by pre-serializing game state data and constants in the game compilation phase of the tool-chain. Binary images of structures load much faster as no code needs to be executed during load. Seamless scene changes are possible as code and data resources for a scene can be loaded without the use of precious CPU cycles. On consoles, this is the preferred method.

 

Games usually load from DVD, which has little tolerance for seeking. Large game projects usually have banks of servers to execute game-specific processes like lightmapping, collision data building, and AI routefinding table construction. There are a number of distributed build systems available to help developers build large code and data systems.

 

For example, it is possible to convert game data into binary images of in-game structures that can be loaded in seconds in megabyte-sized blocks direct from DVD. Often for DVD efficiency, data are duplicated to avoid doing seeks. It is a false economy to store a texture only once on a DVD, for example, as this will cause game loading times to multiply.

 

 

Memory Management

 

High-performance games do their own memory management. Calling malloc or the default new in a game loop is considered irresponsible. Memory fragmentation is the biggest cause of console game crashes and on PC games will cause slow-downs after periods of gameplay. Heap management systems range from simple “hunk” allocators to systems that sort block sizes by location in memory and can allocate from pools in a few cycles. Debug information is usually a part of heap management. It is useful to see which systems are allocating blocks of memory.

 

Avoiding Ransom Blocks which prevent the merging of two large memory blocks is important. A scene should be given its own memory area to allocate from. This will mean that when the scene is torn down, the entire area can be freed without fear of fragmentation.

Trackback Address :: http://joyholic.kr/trackback/159 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/13 13:52

문자셋의 종류와 특성

가장 대표적인 문자셋에 해당하는 아스키코드(ASCII CODE)와 유니코드(UNICODE)에 대해서

간단히 언급하고자 한다. 아스키코드는 미국에서 정의하고 있는 표준이다. 알파벳의 개수는

26개이다. 여기다가 몇몇 확장 문자를 포함해도 총 256개를 넘지 않는다. 그래서 1바이트를

가지고도 충분히 표현할 수 있다. 다시 말해서 아스키코드는 1바이트로 표현이 된다.

문제는 영어가 아닌 다른 국가에서 사용하는 문자들을 표현하는 일이다. 우리나라를 예로 들어

보자. 컴퓨터에 한글을 인식시키기 위해서는 글자 하나하나마다 값(숫자)을 지정해 줘야만 한다.

아스키코드처럼 말이다. 그렇다고 해서 미국에서 영어를 표현하기 위해 정의해 놓은 아스키코드값을

한글로 표현하는 데에 사용할 수도 없는 일이다. 그래서 등장한 것이 유니코드이다.

유니코드는 문자열을 표현하는데 있어서 균일하게 2바이트를 사용한다. 2바이트면 나타낼 수 있는

문자의 종류가 65,536개에 이른다. 이 정도면, 영어와 한글은 물론이거니와 전세계의 거의 모든

문자와 다양한 종류의 기호를 표현할 수가 있다.

그럼 이제 문자셋(Set)에 대해 조금 더 구체적으로 들어가 보겠다. 문자셋이란 문자들의 집합,

다시 말해서 "약속된 문자의 표현방법" 을 의미한다. 문자셋은 그 종류에 따라서 다음과 같이 크게

세 가지 형태로 나뉘어진다.


[SBCS(Single Byte Character Set)]

문자를 표현하는데 있어서 1바이트만을 사용하는 방식이다. 우리에게 가장 익숙한 아스키 코드가

대표적인 SBCS에 해당된다.


[MBCS(Multi Byte Character Set)]

문자를 표현하는데 있어서 동일한 바이트 수를 적용하는 것이 아니라, 다양한 바이트 수를 사용해서

문자를 표현하는 방식이다. 아스키코드에서 정의하고 있는 문자를 표현할 때에는 1바이트로 처리하고,

아스키코드에서 정의하지 않은 다른 문자를 표현할때에만 2바이트로 처리하는 방식이다. 모든 문자를

2바이트로 처리하는 유니코드와 비교하면 상당히 효율적으로 문자를 표현한다는 느낌을 받을 수 있다.

그러나 그 만큼 프로그램을 구현하는 데 있어서 세심한 주의를 기울여야 한다.


[WBCS(Wide Byte Character Set)]

모든 문자를 2바이트로 처리하는 문자셋이다. 유니코드가 WBCS방식에 해당한다.


- WBCS 기반의 프로그래밍 -

WBCS 기반(유니코드 기반)으로 프로그래밍을 하기 위해서는 몇 가지 신경 쓸 일이 있다.

[char을 대신하는 wchar_t]

문자를 표현하는 데 사용되는 자료형 char를 대신해서 자료형 wchar_t를 사용해야 한다. char형

변수는 1바이트 메모리 공간만 할당되지만, wchar_t형 변수는 2바이트 메모리 공간이 할당된다.

wchar_t은 다음과 같은 형태로 선언되어 있는 자료형이다.

typedef unsigned short wchar_t;

["ABC"를 대신하는 L"ABC"]

다음은 자료형 wchar_t를 이용한 문자열의 선언을 보여준다.

wchar_t str[] = "ABC";

만약에 이러한 형태로 문자열을 선언한다면 문제가 발생한다. 왜냐하면 배열 str은 유니코드 문자열을

저장할 준비가 되어 있음에도 불구하고, 대입 연산자의 오른쪽에 존재하는 문자열은 여전히 MBCS 기반

문자열이기 때문이다. 그러므로 이 문자을 다음과 같은 형태로 변경해야 한다.

wchar_t str[] = L"ABC";

문자열 앞에 선언된 문자 L은 "이어서 등장하는 문자열을 유니코드 기반(WBCS 기반)으로 표현하라" 는

의미를 지닌다. 따라서 이 경우에는 문자열 "ABC"는 널(NULL)문자를 포함해서 총 8바이트로 표현된다.

유니코드에서는 문자열 끝을 의미하는 널 문자까지도 2바이트로 처리된다.

다음 표는 SBCS 기반 문자열 조작 함수와, 이에 대응하는 WBCS 기반 문자열 조작 함수를 정리해서

보여준다.

 SBCS 함수  WBCS 기반의 문자열 조작 함수
 strlen  size_t wcslen(const wchar_t* string);
 strcpy  wchar_t* wcscpy(wchar_t* dest, const wchar_t* src);
 strncpy  wchar_t* wcsncpy(wchar_t* dest, const wchar_t* src, size_t cnt);
 strcat  wchar_t* wcscat(wchar_t* dest, const wchar_t* src);
 strncat  wchar_t* wcsncat(wchar_t* dest, const wchar_t* src, size_t cnt);
 strcmp  int wcscmp(const wchar_t* s1, const wchar_t* s2);
 strncmp  int wcsncmp(const wchar_t* s1, const wchar_t* s2, size_t cnt);


완전한 유니코드 기반으로

Windows 2000 이상의 운영체제는 기본적으로 유니코드를 지원할 뿐만 아니라 내부적으로 모든 문자열을

유니코드 기반으로 처리한다. SBSC 기반으로 문자열을 처리하면 운영체제는 전달되는 문자열을 내부적

으로 유니코드 형식으로 변환한다. 이는 프로그램 성능에 다소 영향을 미치는 요소가 될 수 있다.

 SBCS 함수  WBCS 기반의 문자열 입 * 출력 함수
 printf  int wprintf(const wchar_t* format [, argument]...);
 scanf  int wscanf(const wchar_t* format [, argument]...);
 fgets  wchar_t* fgetws(wchar_t* string, int n, FILE* stream);
 fputs  int fputws(const wchar_t* string, FILE* stream);


MBCS와 WBCS의 동시 지원

프로그램 구현에 있어서 MBCS 기반으로 할 것이냐, 아니면 WBCS 기반으로 할 것이냐를 결정짓는 일은

골치 아픈 일이 아닐 수 없다. 물론 WBCS 기반으로 구현하는 것이 여러모로 좋을 듯 하나, 아직까지 현존

하는 시스템 모두가 완벽히 유니코드를 기반을 지원하는 것이 아니기 때문에 문제가 발생할 소지가 높다.

프로그램은 한번만 구현하고, 별다른 변경없이 MBCS 기반으로 돌아가는 형태로도, WBCS 기반으로

돌아가는 형태로도 컴파일 가능하다면 아주 만족스러울 것이다. 이제 그 방법을 소개하고자 한다.


- Windows에서 정의하고 있는 자료형 -

Windows에서는 typedef 키워드를 통하여 몇몇 기본 자료형에 Windows 스타일의 새로운 이름을 정의하고

있다. 다음은 Windows 스타일의 자료형 CHAR와 WCHAR가 어떻게 정의되어 있는지를 보여준다. 물론

Windows.h에 정의되어 있다.

typedef char CHAR;

typedef wchar_t WCHAR;

다음은 문자열의 주소값을 저장할 수 있는 Windows 스타일의 자료형이 어떻게 정의되어 있는지를 보여준다.

#define CONST const


typedef CHAR* LPSTR;

typedef CONST CHAR* LPCSTR;


typedef WCHAR* LPWSTR;

typedef CONST WCHAR* LPCWSTR;


- MBCS와 WBCS(유니코드)를 동시에 지원하기 위한 매크로 -

Windows에서는 MBCS와 WBCS를 동시에 수용하는 형태의 프로그램 구현을 위해서 매크로를 정의하고 있다.

다음은 Windows에 선언되어 있는 내용을 보기 좋은 구조로 간략화한 것이다.

#ifdef UNICODE

 typedef WCHAR         TCHAR;

 typedef LPWSTR        LPTSTR;

 typedef LPCWSTR      LPCTSTR;

#else

 typedef CHAR            TCHAR;

 typedef LPSTR           LPTSTR;

 typedef LPCTSTR       LPCTSTR;

#endif


#ifdef _UNICODE

 #define __T(x) L ## x

#else

 #define __T(x) x

#endif


#define _T(x)         __T(x)

#define _TEXT(x)   __T(x)


매크로 UINCODE가 정의되어 있다면

TCHAR arr[10]; --> WCHAR arr[10]; --> wchar_t arr[10];

매크로 UINCODE가 정의되어 있지 않다면

TCHAR arr[10]; --> CHAR arr[10]; --> char arr[10];


매크로 _UNICODE가 정의되어 있다면

_T("TEST"); --> __T("TEST"); --> L"TEST"

매크로 _UNICODE가 정의되어 있지 않다면

_T("TEST"); --> __T("TEST"); --> "TEST"


- MBCS와 WBCS(유니코드)를 동시에 지원하기 위한 함수들 -

다음은 tchar.h에 선언되어 있는 함수 이름과 관련된 매크로이다.

#ifdef _UNICODE

 #define _tmain      wmain

 #define _tcslen     wcslen

 #define _tcscat     wcscat

 #define _tcscpy    wcscpy

 #define _tcsncpy  wcsncpy

 #define _tcscmp   wcscmp

 #define _tcsncmp  wcsncmp

 #define _tprintf      wprintf

 #define _tscanf     wscanf

 #define _fgetts      fgetws

 #define _fputts      fputws

#else

 #define _tmain      main

 #define _tcslen     strlen

 #define _tcscat     strcat

 #define _tcscpy    strcpy

 #define _tcsncpy  strncpy

 #define _tcscmp   strcmp

 #define _tcsncmp  strncmp

 #define _tprintf      printf

 #define _tscanf     scanf

 #define _fgetts      fgets

 #define _fputts      fputs

#endif


#define UNICODE

#define _UNICODE


#include <stdio.h>

#include <tchar.h>

#include <windows.h>


int _tmain(int argc, TCHAR* argv[])

{

 LPTSTR str1 = _T("MBCS or WBCS 1");

 TCHAR str2[] = _T("MBCS or WBCS 2");

 TCHAR str3[100];

 TCHAR str4[50];


 LPCTSTR pStr = str1;


 _tprintf(_T("string size : %d \n", sizeof(str2));

 _tprintf(_T("string length : %d \n", _tcslen(pStr));


 _fputts(_T("Input String 1 : "), stdout);

 _tscanf(_T("%s"), str3);

 _fputts(_T("Input String 2 : "), stdout);

 _tscanf(_T("%s"), str4);


 _tcscat(str3, str4);

 _tprintf(_T("String1 + String2 : %s \n"), str3);


 return 0;

}

main조차도 _tmain으로 선언되어 있다. 따라서 매크로 _UNICODE의 선언에 따라 MBCS 기반의

main이 될 수도 있고, 유니코드 기반의 wmain이 될수도 있다.

Trackback Address :: http://joyholic.kr/trackback/158 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/12 00:15
임베디드 OS의 주요 개념 및 테크&팁
임베디드 시스템의 성능이 커지면서 기존에 간단한 펌웨어 형식으로 구현되었던 임베디드 프로그램이 점차 해야 할 일이 많아지고 복잡하게 된다. 이는 순차적인 프로그램으로는 해결하기 어려운 단계로 발전하고 결국 임베디드 시스템에도 운영체제의 개념이 필요하게 된다. 임베디드 시스템의 대부분 특성상 실시간이라는 요소를 만족해야 하는 점 때문에 많은 임베디드 OS는 RTOS의 속성을 가진다. 다음에 설명하는 주요 개념들은 여러분이 임베디드 OS를 이용해 임베디드 시스템을 개발하기 위하여 반드시 알아두어야 할 여러 개념 중 중요한 개념들을 선정하여 설명한 것이다.


[ 임베디드 환경의 파일 시스템 ]

파일 시스템이란 저장장치에 파일을 관리하는 시스템을 말한다. 임베디드 시스템에서 주로 사용하는 저장장치는 보통 네트워크를 통한 외부 서버의 저장장치, 타겟 내의 일반 메모리 및 플래시 메모리 등을 말하며 각각 장치의 특성상 다른 파일 시스템이 올라가게 된다. 외부 서버로부터는 저장 장치에 대한 파일 시스템으로는 NFS(Network File System)라는 것이 있는데, 이는 네트워크를 통해 마치 외부 서버의 파일 시스템을 로컬의 파일 시스템처럼 사용할 수 있게 해주는 파일 시스템이다.

일반 메모리에 대한 파일 시스템은 RAMFS(RAm File System)이라 하여 보통 부트 롬 내에 파일 시스템 이미지를 저장해두고 시스템이 부팅시 일반 메모리에 그 파일들을 모두 복사한 다음 RAMFS를 통하여 파일을 유지 관리하는 파일 시스템이다. 단 일반 메모리에 구현돼 있으므로 타겟의 전원이 없어지면 그 내용이 소실된다.

플래시 메모리는 일반 메모리와는 다르게 전원이 나가도 그 내용이 보전되는 특성이 있으므로 한번 파일 시스템이 구현되면 그 내용이 보존되는 특징이 있다. 하지만 플래시 메모리의 특성상 기록 및 삭제의 횟수 제한이 있으므로 파일 시스템 내에서 이를 효율적으로 관리해 주는 부분이 고려되어야 한다.


스케쥴러
임베디드 OS는 보통 태스크(Task)라 불리는 프로그램 수행 단위를 가지게 된다. 보통 임베디드 시스템에서는 복수 개의 태스크가 동시에 수행되는 환경을 가지게 되며 이때 OS 내부의 스케쥴러에 의해서 다음 번에 수행되어야 할 태스크를 선택하게 된다. 이때 사용되는 임베디드 OS의 스케쥴링 알고리즘으로는 다양한 알고리즘이 나와 있지만 구현상의 문제 등으로 인해 실제 대부분의 임베디드 OS에서는 우선순위에 기반을 둔 스케쥴링 알고리즘을 사용한다.

대표적인 우선순위 알고리즘으로 FIFO(First In First Out)와 라운드로빈 방식이 있다. FIFO의 알고리즘은 동일한 우선순위를 가진 태스크들이 존재시 먼저 시작한 태스크가 종료될 때까지 다음 번 수행된 태스크가 스케쥴링을 받지 못하는 스케쥴링 알고리즘이며, 라운드로빈 방식은 동일한 우선순위를 가진 태스크들이 존재할 경우 각각 미리 정해진 타임슬라이스(time-slice)만큼씩 차례로 스케쥴링이 되는 알고리즘이다.

프리엠티브 커널
프리엠티브 커널(Preemptive kernel)은 어떤 태스크가 수행되고 있을 경우 커널이 강제로 그 태스크의 수행을 중지시키고 다른 태스크의 기능을 수행시킬 수 있는 능력을 지닌 커널을 말한다. Non-pree mptive 커널에 비해 interrupt latency가 조금 길어지는 단점이 있긴 하지만 대부분의 임베디드 OS에서 프리엠티브 커널을 채택하는 이유는 우선순위가 높은 태스크가 먼저 수행될 수 있다는 점 때문이다. 이는 결국 커널의 안정성을 높게 유지시킨다는 점에서 좋은 점이 될 수 있다.

상호 배제
하나의 태스크가 한 자원을 점유하여 사용중에 또다른 태스크가 접근하여 사용중인 자원에 무단으로 접근 시도 변경을 가한다면 곧 그 자원은 예측불허의 상황으로 치달을 수밖에 없다. 이런 부분을 임계 지역(critical section)이라 명하며, 한 자원이 점유중일 경우 나머지 자원들은 점유하고 있던 태스크가 자원을 모두 사용하고 사용권을 반환하기 전까지 모두 접근이 지연돼야 하는 지역을 말한다. 따라서 임계 지역은 상호 배제(mutual exclusion)로써 보호해야 하며 임베디드 OS는 상호 배제를 위해 인터럽트 Enable/Disable이나 세마포어의 기능으로 임계 지역을 보호할 수 있다.


[ 세마포어 ]

세마포어(semaphore)는 60년대 중반 Dijkstra가 고안한 메커니즘이다. 이는 태스크간에 상호 공유하는 변수이며, 임계 지역을 세마포어로 보호하면 태스크간 상호 배제을 구현할 수 있다. 기본적인 알고리즘은 먼저 세마포어 변수를 선언한 후 임계 지역에 전에 세마포어를 얻기를 시도한다. 만일 아무도 세마포어 얻기를 시도하지 않았으면 바로 임계 지역에 들어갈 수 있으나 다른 태스크가 세마포어를 얻어서 임계 지역에 있으면 나중에 시도한 태스크는 세마포어를 풀어줄 때까지 블럭 상태로 되게 된다. 이후 세마포어가 풀리면 블럭된 태스크는 세마포어를 얻어 임계 지역에 진입할 수 있게 된다.

세마포어는 초기화시 동시에 진입할 수 있는 개수를 명시할 수 있는데 그 개수가 하나인 세마포어에 대하여 바이너리 세마포어(binary semaphore)라 하며 그 이상의 값을 명시하면 카운팅 세마포어(counting semaphore)라 한다.


태스크 커뮤니케이션
임베디드 OS에서의 태스크들은 종종 서로 데이터를 교환해야 할 경우가 생기게 된다. 이럴 경우 태스크간 통신을 통하여 교환할 수 있으며 이 기능은 OS에서 지원하는 여러 도구들에 의해 수행이 된다. 대표적인 방법으로는 시스템 전역의 변수를 선언하여 사용방법이 있으나 이 경우에는 변수 자체가 임계 지역이 되므로 인터럽트 Enable/ Disable이나 세마포어 등으로 상호 배제를 해야 한다. 그 밖의 방법으로는 큐, 파이프, 메일박스, 공유 메모리 등이 있으며 이들은 커널에 의해 자동으로 상호 배제가 이루어짐으로 임계 지역이 보호된다.


[ JTAG란? ]

TAG(Joint Test Access Group)는 IEEE 표준 1149.1-1990 Test Access Port and Boundary-Scan Architecture에 근거한 규격으로 CPU에서 직접 지원하는 디버깅을 위한 인터페이스로 현재 CPU의 상태를 점검하거나 프로그램의 디버깅, 플래시 메모리에 새로운 자료를 저장할 때 주로 사용한다.

Trackback Address :: http://joyholic.kr/trackback/141 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/12 00:02

실질적인 컴파일 이전에 미리 처리되는 문장으로 선행처리기라고도 한다.
컴파일러는 사용자가 작성한 코드를 컴파일하기에 앞서 전처리문에서 정의해 놓은 작업들을 먼저 수행한다.

종류로는 #define, #if, #ifdef, #ifndef, #defined, #undef 등이 있다.
이것은 기존에 있는 방대한 소스 코드를 지우지 않고 활성화와 비활성화하는 데에 가장 많이 이용된다.
즉, 기존에 있는 소스 코드를 건드리지 않고 부분적인 컴파일을 하는 것이다.

C의 전처리문이 오는 줄(Line)의 첫 문자는 항상 '#'으로 시작한다.

ANSI 표준에 따른 C의 전처리문의 종류
- 파일 처리를 위한 전처리문 : #include
- 형태 정의를 위한 전처리문 : #define, #undef
- 조건 처리를 위한 전처리문 : #if, #ifdef, #ifndef, #else, #elif, #endif
- 에러 처리를 위한 전처리문 : #error
- 디버깅을 위한 전처리문 : #line
- 컴파일 옵션 처리를 위한 전처리문 : #pragma

조건 처리를 위한 전처리문은 어떤 조건에 대한 검사를 하고 그 결과를 참(0 이 아닌 값) 또는 거짓(0)으로 돌려준다.
#if : ...이 참이라면
#ifdef : ...이 정의되어 있다면
#else : #if나 #ifdef에 대응된다.
#elif : 'else + if'의 의미
#endif : #if, #ifdef, #infdef이 끝났음을 알린다.

#include
헤더 파일과 같은 외부 파일을 읽어서 포함시키고자 할 때 사용된다. 이때의 파일은 이진 파일(Binary file)이 아닌 C의 소스 파일과 같은 형태의 일반 문서 파일을 말한다:
#include <stdio.h>        /* 이 위치에 stdio.h라는 파일을 포함시킨다. */
#include "text.h"           /* 이 위치에 text.h라는 파일을 포함시킨다. */

<...>을 사용할 때와 ...을 사용할 때의 차이점은 <...>은 컴파일러의 표준 포함 파일 디렉토리(또는 사용자가 별도로 지정해 준)에서 파일을 찾는 것을 기본으로 한다. 그리고 ...을 사용했을 때는 현재의 디렉토리를 기본으로 파일을 찾게 된다. 아예 디렉토리를 같이 지정할 수도 있다:
#include <C:\MYDIR\MYHEAD.H>
#include "C:\MYDIR\MYHEAD.H"

#define
상수 값을 지정하기 위한 예약어로 구문의 상수로 치환한다. 또한 #define은 함수 역활과 비슷하게 아래와 같이 쓰일 수 있다:
#define SUM(x) ((x) = (x) + (x))

#define으로 정의할 수 있는 것은 숫자만이 아니다:
#define MYNAME "Young Hee"

이렇게 #define으로 정의된 것은 일반적인 변수와는 다르다. 그 차이는 명백하다:
#define MYNAME "Turbo"
char my_name[] = "Turbo"

MYNAME은 전처리문으로 my_name은 문자형 배열 변수로 정의되었다:
printf(MYNAME);
printf(MYNAME);
printf(my_name);
printf(my_name);

이것을 전처리한 상태는 다음과 같이 될 것이다:
printf("Turbo");
printf("Turbo");
printf(my_name);
printf(my_name);

이런 결과에서 우리가 유추해 볼 수 있는 것은 전처리 명령을 사용했을 경우 "Turbo"라는 동일한 동작에 대해서 두개의 똑같은 문자열이 사용됐고, 변수를 사용했을 경우에는 하나의 문자열을 가지고 두번을 사용하고 있다는 것이다. 결과적으로 이런 경우에는 전처리문을 사용했을 경우 메모리 낭비를 가져 온다는 것을 알 수 있다.

#undef
#define으로 이미 정의된 매크로를 무효화한다:
#define ADD(a, b) (a + b)
#undef ADD(a, b)

앞으로 사용되는 ADD(...)는 undefined symbol이 되어 에러 처리된다.

#if ~ #endif
#if 구문은 if랑 아주 비슷하다. 이것은 어떠한 구문을 컴파일 할지 안할지를 지정할 수 있다:
#define A 1
#if A
source code ...
#endif

위 source code 부분은 컴파일이 된다. if문에서와 같이 참, 거짓을 구분하여 컴파일이 된다. 위에서 A값은 1 즉 0보다 큰 수이기 때문에 참인 것이다. 직접 아래와 같이 하면 거짓이기 때문에 source code 부분은 컴파일이 되지 않는다:
#if 0
source code ...
#endif

#ifdef ~ #endif
컴파일 할 때
#define MYDEF                /* MYDEF는 값은 가지지 않았지만 어쨋든 정의는 되었다 */

#ifdef YOURDEF              /* 만약 YOURDEF가 정의되어 있다면... */
#define BASE 10             /* BASE == 10 */
#elif MYDEF                    /* 그외에 MYDEF가 정의되었다면... */
#define BASE 2               /* BASE == 2 */
#endif

BASE는 상수 2로 치환되어 전처리기가 컴파일러에게 넘겨준다.

#ifndef 헤더명_H__ ~ #endif
헤더 파일이 겹치는 것을 막기 위한 일종의 매크로이다. 예를 들어, 헤더 파일에 어떤 클래스의 인터페이스 선언을 넣었다고 하자. 이 클래스 인터페이스에서 다른 파일의 프로토타입이 필요해서 다른 A 파일을 include 하고 있는데 이 헤더 파일을 include 하는 파일에서 A라는 헤더 파일을 이미 include 하고 있다면 두번 define한 것이 된다. 그러면 SYNTEX 에러가 난다. 그래서 그런 것을 막는 방법의 하나로 #ifndef을 사용한다. 이전에 include되어 있으면 #endif 쪽으로 점프해 버려 결국 한번 선언되는 것이다:
#include  <stdio.h>    ------ (a)
#include  <stdio.h>    ------ (b)

이렇게 두번 썼다고 하자. 그런데 앞에 이미 include를 했는데 밑에 또 한다면 문제가 된다. 컴파일러가 검사해야할 코드량도 많아진다. 그래서 stdio.h에는
#ifndef STDIO_H__
#define STDIO_H__
가 선언되어 있다. 만약 STDIO_H가 선언되어 있지 않다면 선언한다는 뜻이다. 그 뒤 (b)에서는 이미 (a)쪽에서 STDIO_H__ 을 선언한 상태이기 때문에 전처리기 쪽에서 무시해버린다. 그러므로 컴파일러는 (a)만 검사한다.

#defined
define이 여러 개 되어 있는지를 검사할 때 쓴다. 이것은 여러 개를 동시에 검사 할 수 있다:
#if (defined A) || (defined B)

#ifdef와 #if defined의 차이
#ifdef은 정의가 되어 있는지를 테스트 하기 때문에, 한번에 여러 개를 사용할 수 없다:
#ifdef name

여러 개가 정의되어 있는지를 테스트 하기 위해서 #if defined를 사용할 수 있다:
#if defined(MACRO1) || defined(MACRO2)

#if는 ||로 중첩해서 사용할 수 있다. 형식이, #if expression이므로 C 표현이 올 수 있다:
#if (MACRO1) || (MACRO2)

#error
소스 라인에 직접 에러 메세지를 출력한다. 전처리기가 #error 문을 만나면 그 즉시 컴파일을 중단하고 다음과 같은 에러 메시지를 출력한다:
ERROR : XXXXX.c ########: Error directive: 내용
- XXXXX.c --> 현재 컴파일 중인 파일 명
- ####### --> 전처리기가 #error 문을 만난 시점에서의 행 번호(헤더 포함)

#ifdef __LARGE__
#error This program must be compiled in LARGE memory model!
#endif

이 내용은 만일 프로그램이 LARGE 모델이 아니라면 "#error" 뒤에 표시된 메세지를 출력하고 컴파일을 중지하게 된다.

#line
이 명령은 소스 코드의 행 번호를 지정하기 위한 것으로 주로 컴파일러에 의해 미리 정의된 __LINE__과 함께 사용된다.
__LINE__과 __FILE__을 각각 행 번호와 파일 명으로 변경한다:
#include <stdio.h>
#define DEBUG

void main(void)
{
        int count = 100;

        #line 100               /* 다음 줄번호를 100으로 설정한다 */
                                   /* <-- 이 줄의 번호가 100이다 */
        #ifdef DEBUG        /* <-- 이 줄의 번호가 101이다 */
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif

        count = count * count - 56;
        #ifdef DEBUG
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif
        count = count / 2 + 48;
        #ifdef DEBUG
        printf("line:%d, count = %d\n", __LINE__, count);
        #endif
}

#pragma
컴파일 옵션의 지정. 컴파일러 작성자에 의해서 정의된 다양한 명령을 컴파일러에게 제공하기 위해 사용되는 지시어이다. 컴파일러의 여러 가지 옵션을 명령행에서가 아닌 코드에서 직접 설정한다. #pragma는 함수의 바로 앞에 오며 그 함수에만 영향을 준다.
Turbo C는 9개의 #pragma 문(warn, inline, saveregs, exit, argsused, hdrfile, hdrstop, option, startup)을 지원하고 있다:

#pragma inline
컴파일할 때 어셈블러를 통해서 하도록 지시한다. 즉, 인라인 어셈블리 코드가 프로그램에 있음을 알려준다(명령행 상에서 '-B' 옵션).

#pragma saveregs
이 홉션은 휴즈 메모리 모델에 대해 컴파일된 함수에게 모든 레지스터를 저장하도록 한다.

#pragma warn
이 지시어는 Turbo C에게 경고 메시지 옵션을 무시하도록 한다.

#pragma warn -par
이는 매개 변수(parAMETER)가 사용되지 않았다고 경고(warnING)를 내지 못하도록 한다. 이와 반대되는 표현은
#pragma warn +par

경고의 내용 앞에 (+)를 사용하면 경고를 낼 수 있도록 설정하고 (-)를 사용하면 경고를 내지 못하도록 하는 것은 모든 경고에 대해 동일하다. 명령 행에서는 "-wxxx"로 경고를 설정하고 "-w-xxx"로 경고를 해제한다. 경고의 종류는 무척 많은데 자주 사용되는 것을 아래에 나타냈다. 모든 것을 알고 싶다면 컴파일러 User's Guide의 명령행 컴파일러 부분을 참고하기 바란다:
par : 전해진 파라미터가 사용되지 않음
rvl : void 형이 아닌 함수에서 리턴 값이 없음
aus : 변수에 값을 할당했으나 사용하지 않았음
voi : void 형 함수에서 리턴 값이 사용되었음
sig : 부호 비트(MSB)가 고려되지 않은 형 변환(type-castion)에서 부호 비트를 소실할 수 있음

Standard C pre-defined symbols

__FILE__ a string that holds the path/name of the compiled file
__LINE__ an integer that holds the number of the current line number
__DATE__ a string(Mmm dd yyyy) that holds the current system date
__TIME__ a string(hh:mm:ss) that holds the current system time
__STDC__ defined as the value '1' if the compiler conforms with the ANSI C standard
__cplusplus determines if your compiler is in C or C++ mode. Usually used in headers

#include <stdio.h>

void main(void)
{  
        printf("The path/name of this file is %s\n", __FILE__);  
        printf("The current line is %d\n", __LINE__);  
        printf("The current system date is %s\n", __DATE__);  
        printf("The current system time is %s\n", __TIME__);  

        #ifdef __STDC__  
        printf("The compiler conforms with the ANSI C standard\n");  
        #else  
        printf("The compiler doesn't conform with the ANSI C standard\n");  
        #endif  

        #ifdef __cplusplus  
        printf("The compiler is working with C++\n");  
        #else  
        printf("The compiler is working with C\n");  
        #endif 


프로그래머들 마다 코딩 스타일(암시적 약속)이 있다. 보통 매크로, const 변수는 대문자로 적는 것이 원칙이다. 매크로 함수와 일반 함수, 매크로 대상체(object-like macro)와 일반 변수를 구분하기 쉽게 해주는 것이기 때문이다.

#define STDIO_H_
왜 뒤에 _를 붙였을까? 이것도 하나의 암시적 약속이다. 컴파일러 제작 회사는 매크로를 정의할 때 사용자들과 이름이 충돌이 나지 않게 하기 위해서 대부분 _를 뒤어 덧붙인다. 또한 _를 하나 혹은 두 개 연속으로 시작하는 것은 컴파일러 내부에서 사용하는 매크로라는 성격이 강하다. 물론 강제적인 뜻은 없으며 단지 관습상 그렇다. 왜 이것이 관습이 되었나 하면 보통 매크로 변수 이름이나 함수 이름을 지을 때 뒤에 _를 붙이지 않기 때문이다. 그래서 함수 제작자들이 _를 단골로 붙였다.

Trackback Address :: http://joyholic.kr/trackback/139 관련글 쓰기
Tracked from IPLAB 人 | 2008/09/18 16:05 | DEL
ANSI 표준에 따른 C의 전처리문의 종류 - 파일 처리를 위한 전처리문 : #include - 형태 정의를 위한 전처리문 : #define, #undef - 조건 처리를 위한 전처리문 : #if, #ifdef, #ifndef, #else, #elif, #endif - 에러 처리를 위한 전처리문 : #error - 디버깅을 위한 전처리문 : #line - 컴파일 옵션 처리를 위한 전처리문 : #pragma 다양한 전처리문에 대해서 정리해 놓은 글이네..
Tracked from To.World | 2008/11/11 13:55 | DEL
오늘 오전 내내 전처리 과정때문에 말썽을 좀 많이 피웠습니다.. 그래서 좋은 자료 트랙백 해 놓습니다.
Tracked from ttagui님의 블로그 | 2009/02/20 14:34 | DEL
실질적인 컴파일 이전에 미리 처리되는 문장으로 선행처리기라고도 한다.컴파일러는 사용자가 작성한 코드를 컴파일하기에 앞서 전처리문에서 정의해 놓은 작업들을 먼저 수행한다.종류로는 #define, #if, #ifdef, #ifndef, #defined, #undef 등이 있다.이것은 기존에 있는 방대한 소스 코드를 지우지 않고 활성화와 비활성화하는 데에 가장
Tracked from standyhon님의블로그 | 2011/01/24 06:03 | DEL
실질적인 컴파일 이전에 미리 처리되는 문장으로 선행처리기라고도 한다.컴파일러는 사용자가 작성한 코드를 컴파일하기에 앞서 전처리문에서 정의해 놓은 작업들을 먼저 수행한다.종류로는 #define, #if, #ifdef, #ifndef, #defined, #undef 등이 있다.이것은 기존에 있는 방대한 소스 코드를 지우지 않고 활성화와 비활성화하는 데에 가장
Tracked from All about computer | 2011/04/04 05:43 | DEL
출처:http://joyholic.kr/139 실질적인 컴파일 이전에 미리 처리되는 문장으로 선행처리기라고도 한다. 컴파일러는 사용자가 작성한 코드를 컴파일하기에 앞서 전처리문에서 정의해 놓은 작업들을 먼저 수행한다. 종류로는 #define, #if, #ifdef, #ifndef, #defined, #undef 등이 있다. 이것은 기존에 있는 방대한 소스 코드를 지우지 않고 활성화와 비활성화하는 데에 가장 많이 이용된다. 즉, 기존에 있는 소스 코...
원문 작성하신 분 주소 : http://joyholic.kr/139 컴파일러는 사용자가 작성한 코드를 컴파일하기에 앞서 전처리문에서 정의해 놓은 문장들을 먼저 처리한다. 종류로는 #include, #define, #if, #error, #line, ..
릭스 | 2008/11/28 11:17 | PERMALINK | EDIT/DEL | REPLY
상당히 유용한 글 잘보고 갑니다.
아주 쏙쏙 들어옵니다.
여기서 질문? | 2009/02/07 13:22 | PERMALINK | EDIT/DEL | REPLY
혹시 cpp 파일에서 전처리 문을 썼을때 그 사이에 있는 소스코드에서 변수의 선언이나 정의로 이동 하는 기능이 활성화 되지 않는데 활성화 되게 하는 방법을 혹시 알고 계신지 궁금하네요
Name
Password
Homepage
Secret
2007/09/11 11:36
1. 서론
이 글은 DLL(Dynamic Link Library)에 대하여 다룬다. 이 글을 제대로 이해하기 위해서
는 Win32 프로그래밍에 대한 중급 이상의 지식, C 프로그래밍에 대한 고급 수준의 지식,
C++ 프로그래밍에 대한 중급 이상의 지식이 요구된다.
여러 Win32 프로그래밍 책에서 DLL이라는 주제를 다루기는 하지만, 잘못 나온 내용도
종종 있고, 전역 변수, 전역 함수, 클래스를 export하는 다양한 예도 별로 나와있질 않다.
따라서 실제로 그 정도 내용만으로는 궁금증을 다 해소하기가 어렵다. 이 문서에서는 DLL
에서 전역 변수, 함수, 클래스를 정의해놓고 외부에서 접근하는 예를 중점적으로 설명한다.
여기서 다루는 DLL은 순수한 C/C++로 만들어진 DLL을 말한다. Managed Code로 작성
된 DLL이나, MFC/ATL을 사용한 DLL, COM으로 만들어진 DLL은 다루지 않는다.

2. DLL이란?
DLL은 Dynamic Link Library의 줄임말로서 동적 라이브러리(혹은 공유 라이브러리)를
말한다. 먼저 일반적인 라이브러리에 대해 이야기를 해보자.
아주 먼 과거에는 하나의 프로그램은 한 개의 실행 파일로 구성되어 있었다. 물론 지금도
그렇게 만들 수도 있지만, 라이브러리를 이용하는 것이 프로그램 개발의 생산성을 향상시키
기 때문에 현재에는 라이브러리들을 이용하여 개발을 한다.
라이브러리는 크게 정적 라이브러리와 동적 라이브러리로 구분할 수 있다. 정적 라이브러
리는 단순히 object 파일들을 묶어놓은 것에 지나지 않는다. 하나의 function이 들어있는 C
소스 코드를 컴파일하면 확장자가 OBJ인 파일이 생성되는데, 그러한 object 파일을 1개 이
상 묶어서 확장자 LIB를 붙이면 그게 정적 라이브러리이다. 이러한 정적 라이브러리는 링크
시에 실행 파일(EXE 파일)에 그대로 복사되며, 이렇게 만든 EXE 파일을 실행할 때에는 별
도의 라이브러리가 필요치 않게 된다. 왜냐하면 이미 EXE 파일 안에 정적 라이브러리의 코
드들이 다 복사되어 있기 때문이다.
정적 라이브러리를 생성하려면 Visual Studio에서 Static library 프로젝트로 만들면 되는
데, 사실상 컴파일된 1개 이상의 OBJ 파일들을 LIB.EXE라는 프로그램으로 묶어주는 일을
한다. 여기에 대해서 더 자세히 알아보려면 Visual Studio에 포함되어 있는 LIB.EXE 유틸
리티를 직접 실행해 보기 바란다. 이 유틸리티를 사용하면, OBJ 파일들을 묶어 .LIB 파일을
생성하는 것은 물론, 이미 생성된 .LIB 파일에서 다시 OBJ 파일들을 뽑아낼 수도 있다.

정적 라이브러리는 단점이 하나 있는데, 똑같은 코드가 매번 EXE 파일 안에 복사된다는
것이다. 즉, EXE 파일의 크기가 커짐은 물론 실행시 메모리 공간이 절약되지도 않는다. 예
를 들어 정적 라이브러리를 사용한다면 printf()라는 C 라이브러리 함수를 사용하는 모든
프로그램은 printf() 함수의 코드(물론 기계어 형태로)를 모두 포함하고 있어야 한다. 만약
printf()처럼 거의 모든 프로그램이 사용하는 코드를 별도의 파일에 넣어 놓고, 여러 프로그
램에서 공유해서 쓴다면 디스크 공간도 절약되고 실행시에도 메모리 용량도 절약될 것이다.
그런 개념에서 나온 것이 동적 라이브러리인 DLL이다. 같은 코드를 여러 프로그램에서 공
유해서 사용하기 때문에 공유 라이브러리라는 표현도 쓴다.
여러분이 어떤 코드를 DLL로 만들어야겠다고 결정했다면, 거기에는 다음과 같은 이유들
이 있을 수 있다.
첫째, 완벽히 분리된 모듈화를 통해 분업의 생산성을 높이기 위해서이다. 여러 팀원끼리
하나의 프로그램을 만드는 경우에, DLL을 이용하여 모듈화를 하면 완벽한 분업을 이루기가
쉽다. 한 개발자가 하나의 기능을 담당하는 DLL을 만들고, 각자 완벽한 단위 테스트를 한
후에, 나중에 통합을 하면 전체 프로그램이 조화롭게1 작동되게 된다.
둘째, 상황에 따라 다른 리소스를 불러들일 수 있다. DLL은 EXE 파일과 마찬가지로 리소
스(비트맵 이미지, 아이콘, 스트링 테이블 등)를 포함할 수 있다. 만약 한글 윈도우와 영문
윈도우에서 서로 다른 리소스를 로딩해야 하는 상황이라면 이 리소스들을 분리된 DLL에 넣
어놓고 상황에 따라 적당한 DLL을 로딩할 수도 있다. 이 경우에는 DLL에 단순히
DllMain() 함수만 정의해 놓고 그 이외의 어떠한 코드도 넣을 필요가 없다. 그저 Visual
Studio에서 DLL을 하나 만들고 아무 일도 안하는 DllMain() 함수만 하나 넣어놓고, 리소스
를 추가한 후에 컴파일하면 그만이다. 이 문서에서는 이러한 상황에서의 응용은 다루지 않
는다.
셋째, 부분 업그레이드가 쉬워진다. 만약 어떤 하나의 DLL에서 버그가 발견되었다면, 전
체 프로그램을 업데이트할 필요 없이, 해당 DLL만 업데이트를 해주면 된다. 따라서 버그
패치가 한결 수월해진다. 또한 해당 프로그램에 버그를 제공한 프로그래머를 색출해 내기에
도 용이한 점이 있다. 참고로 특정 DLL에 버그를 제공한 프로그래머를 색출하더라도 너무
뭐라고 하지는 마라. eXtreme Programming에서도 “책임을 묻지 않기”라는 대 원칙이 있다.
넷째, 전역 훅킹(global hooking)을 위해서 사용하는 경우가 있다. 이 경우에는 반드시
DLL을 사용해야만 한다. 이 주제는 Win32 프로그래밍에 대한 깊은 이해를 필요로 하므로,
여기서는 다루지 않는다.
1 물론 이론적으로 그러하다.

3. DLL 만들기
간단한 DLL을 실제로 만들어 보자. 여기서는 전역 변수, 전역 함수, 클래스를 export하
는 DLL을 만들어 볼 것이다. 그리고 이후의 장에서는 이 DLL을 불러서 사용하는 방법들을
이야기하겠다.
Visual Studio에서 “MyDLL”이라는 이름의 Win32 프로젝트를 만들고, Application type
은 DLL로, Additional options에서 Empty project를 체크한다. 그렇게 프로젝트를 만들고,
MyDLL.cpp라는 소스 파일을 추가한다. 그리고 그 파일에 다음과 같이 입력한다.
// 전역 변수 export.
extern "C" __declspec(dllexport) char *str = "THE TRUTH IS OUT THERE";
// 전역 함수 export.
extern "C" __declspec(dllexport) int Add(int a, int b) {
return a + b;
}
// 클래스 export.
class __declspec(dllexport) CCalc {
public:
int Add(int a, int b);
};
int CCalc::Add(int a, int b) {
return a + b;
};
// CCalc의 인스턴스를 생성해주는 함수.
extern "C" __declspec(dllexport) CCalc *CreateCalcInstance(void) {
return new CCalc();
}
// CCalc의 인스턴스를 파괴해주는 함수.
extern "C" __declspec(dllexport) void DeleteCalcInstance(CCalc *obj) {
delete obj;
}
전역 변수 str부터 살펴보자. 만약 이 str이라는 변수를 외부에서 사용할 수 있게끔
export할 생각이 아니었다면, 혹은 일반적인 EXE 파일에서의 전역변수였다면 그냥 char
*str = “…”;와 같이 사용하였을 것이다. 그러나 여기에 extern “C”라는 것과
__declspec(dllexport)라는 것이 더 붙어 있다는 것을 볼 수 있을 것이다. 이 2가지 모두
이 변수를 export하기 위해 필요한 것들이다. extern “C”에 대한 설명은 이후로 미루고,
__declspec(dllexport)에 대해 먼저 알아보자. __declspec()은 어떤 선언을 추가적으로 꾸며
주기 위해 사용되는 것으로, declaration specification의 줄임말이다. 괄호 안에는 다양한
키워드가 올 수 있는데, __declspec(dllexport)라고 하면 이 변수를 DLL 외부에서 사용할
수 있게끔 export하겠다는 의미이다.
그 다음 Add()라는 간단한 함수가 있다. 이 함수 역시 str 변수와 마찬가지로 extern “C”
와 __declspec(dllexport)가 붙어 있다. 함수의 경우에도 __declspec(dllexport)는 이 함수
를 DLL 외부에서 사용할 수 있게끔 export하겠다는 의미이다.
그러면 정말 str이라는 변수와 Add()라는 함수가 DLL 외부에서 사용할 수 있게끔,
export가 잘 되었는지 확인해 보도록 하자. 이 프로젝트를 컴파일하고, Visual Studio Tools
아래에 있는 Command Prompt 창을 열어, 생성된 DLL이 있는 디렉토리로 이동해 보자.
그리고 다음과 같이 DUMPBIN.EXE 유틸리티를 사용하여, DLL 파일이 export하고 있는 목
록의 리스트를 보자.
C:\Programming\MyDLL\MyDLL\Release>dumpbin /exports MyDLL.dll
Microsoft (R) COFF/PE Dumper Version 8.00.50215.44
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file MyDLL.dll
File Type: DLL
Section contains the following exports for MyDLL.dll
00000000 characteristics
430E95DF time date stamp Fri Aug 26 13:09:03 2005
0.00 version
1 ordinal base

6 number of functions
6 number of names
ordinal hint RVA name
1 0 00001010
??4CCalc@@QAEAAV0@ABV0@@Z = ??4CCalc@@QAEAAV0@ABV0@@Z
(public: class CCalc & __thiscall CCalc::operator=(class CCalc const &))
2 1 00001020
?Add@CCalc@@QAEHHH@Z = ?Add@CCalc@@QAEHHH@Z (public: int
__thiscall CCalc::Add(int,int))
3 2 00001000 Add = _Add
4 3 00001030 CreateCalcInstance = _CreateCalcInstance
5 4 00001050 DeleteCalcInstance = _DeleteCalcInstance
6 5 00003000 str = _str
Summary
1000 .data
1000 .rdata
1000 .reloc
1000 .rsrc
1000 .text
C:\Programming\MyDLL\MyDLL\Release>
DUMPBIN.EXE 유틸리티에 /exports 옵션을 주면, 해당 DLL 파일이 외부로 export하고
있는 목록들의 리스트를 출력해준다. 위에서 보듯이 Add와 str이라는 심볼(symbol)이
export되어 있음을 알 수 있다.
우리는 변수명으로 str을 쓰고, 함수명으로 Add를 썼고, 이 DLL은 자연스럽게 str과 Add
라는 이름의 심볼을 export하고 있다. 이렇게 될 수 있는 것은 바로 extern “C” 키워드 때
문이다. 만약 extern “C”를 지워버리면 str과 Add라는 이름 대신 아주 이상한 이름이
export되게 될 것이다(직접 해보라). 이렇게 되면 이 DLL을 명시적으로 로딩하여 사용하는
데 문제가 생긴다.
extern “C”는 C++ 컴파일러가 심볼에 대해 name mangling1을 하지 않도록 해준다. 참
1 name decoration이라고도 한다. 위의 dumpbin.exe의 출력에서
?Add@CCalc@@QAEHH
H@Z처럼 함수 이름을 이상하게 만들어 버리는 것을 말한다.

고로 C 컴파일러는 name mangling을 하지 않으므로, 만약 이 소스 파일의 이름을
MyDLL.c라고 했다면 extern “C”는 쓰지 않아도 된다. 그러나 이 소스 파일은 클래스를 포
함하고 있기 때문에 확장자를 c로 하면 컴파일이 제대로 안 될 것이다.
그렇다면 왜 C++ 컴파일러는 심볼 이름을 그대로 내버려 두질 않고 이상하게 바꾸어 버
리는 것일까? 그것은 C++이 객체 지향 언어이기 때문이다. 객체 지향 언어의 3가지 특징
을 이야기하라면 inheritance(상속), encapsulation(캡슐화), polymorphism(다형성)을 이야
기할 수 있다. 여기서 문제가 되는 것이 다형성이다. 다형성은 C++ 언어의 여러 측면을 통
해 그 특징이 나타나는데, 그 중 하나가 overloading된 함수이다. 함수의 overloading은 함
수의 이름도 같고, 반환 타입도 같은데, 파라미터의 개수나 종류가 다른 경우를 말한다. 그
러니까, Add()라는 같은 이름의 함수라도, 파라미터를 2개 받는 것과 3개 받는 것이 공존할
수가 있다는 것이다. 그러나 안타깝게도 DLL이라는 공유 라이브러리 포맷 자체가 애초에
객체지향적 언어를 지원하도록 만들어진 것이 아니다. 따라서 이 DLL은 그러한 오버로딩된
함수를 제대로 분리해서 export할 수 있는 능력이 없다. 그래서 만약에 오버로딩된 2개의
Add() 함수가 있는 경우에 그 함수의 이름을 컴파일러가 마음대로 이상하게 바꾸어서, 2개
의 오버로딩된 함수가 서로 다른 이름을 갖도록 만들어버리는 것이다. 기본적인 C++ 컴파
일러의 작동은 모든 심볼을 name mangling하지만, extern “C”라고 명시적으로 써주면, 컴
파일러는 해당 변수나 함수에 대하여 name mangling을 하지 말라는 의미로 받아들인다. 물
론 extern “C”라고 쓸 때에는 함수의 오버로딩 기능은 포기해야 한다. 함수의 오버로딩을
사용하면서 extern “C”라고 쓰면 컴파일이 되지 않고 에러가 날 것이다.
이제 클래스를 export하는 것에 대해 알아보자. 많은 Win32 프로그래밍 책들에서 클래스
를 export하는 것은 MFC를 이용한 확장 DLL에서나 가능하며, 순수한 C/C++로 만들어진
기본 DLL에서는 불가능하다고 나와 있을 것이다. 그러나 이것은 사실과 다르다. DLL을 만
드는 개발자가 사용하는 컴파일러와 DLL을 사용하는 개발자가 사용하는 컴파일러가 동일한
회사의 동일한 버전이라는 가정만 있다면 클래스를 DLL에 넣어놓고 외부로 export하는 것
에 아무런 문제가 없다. 클래스를 export할 때에는 class 키워드 다음에
__declspec(dllexport)를 넣으면 된다. 여기서는 CCalc라는 클래스를 정의하였으며, 이 클
래스 안에는 Add()라는 함수 하나가 있다. 클래스를 export시킬 때에는 extern “C”를 붙일
수가 없다. 따라서 클래스의 멤버 function의 이름이 mangling되는 것을 막을 수는 없다.
그래서 dumpbin.exe의 출력 결과를 보면
?Add@CCalc@@QAEHHH@Z와 같은 이상한 이
름으로 Add()라는 멤버 함수의 이름이 바뀌어 있는 것을 볼 수 있다. 또 그 위에 보면, 우
리가 만든 적이 없는데
“??4CCalc@@QAEAAV0@ABV0@@Z”라는 함수 같은 것이 하나
더 있는 것을 볼 수 있다. 그것은 컴파일러가 자동적으로 생성해준 CCalc 클래스의 대입
연산자이다.
마지막으로 CreateCalcInstance()와 DeleteCalcInstance()라는 함수가 있다. 이것은 명시

적으로 DLL을 로딩하여 클래스를 사용할 때만 필요하다. 그때 다시 설명하도록 하겠다.
코드를 정상적으로 입력하고 컴파일을 하면 .LIB 파일과 .DLL 파일이 생성된다. 여기서
생성되는 .LIB 파일은 정적 라이브러리의 .LIB와는 다르다. DLL과 함께 생성되는 .LIB 파
일에는 기계어 코드는 전혀 들어있지 않고, 어떤 이름의 함수를 사용하기 위해서는 어떤
DLL이 있어야 된다는 정보 정도가 들어 있다.
이것으로 전역 변수, 전역 함수, 클래스를 export하는 DLL을 만들어 보았다. 이제 이
DLL을 사용하는 클라이언트를 만들어볼 차례이다. DLL은 묵시적 링킹과 명시적 링킹 2가
지 형태로 이용이 가능하다. 먼저 묵시적 링킹을 알아보고, 그 다음에 명시적 링킹을 알아
보도록 하겠다.

4. 묵시적인 DLL 링킹
묵시적인 DLL 링킹이란, EXE 파일을 링크할 때 DLL과 함께 생성된 .LIB 파일을 링크하
는 것을 말한다. 그리고 EXE 파일이 실행될 때에는 DLL 파일이 EXE 파일의 실행과 동시
에 메모리에 로드가 된다. 따라서 적절한 DLL 파일을 찾을 수 없다면 EXE 파일이 실행조
차 되지 않고, DLL을 찾을 수 없다는 에러 메시지가 나오게 된다. 그리고 적절한 변수나 함
수의 선언만으로 DLL 파일에 있는 변수나 함수에 접근할 수가 있다.
정리하면 묵시적 DLL 링킹에서는, EXE 파일의 compile-time에는 적절한 헤더 파일
과 .LIB 파일이 필요하며, 이렇게 만들어진 EXE 파일의 run-time에는 .DLL 파일만 있으면
된다.
묵시적 DLL 링킹을 사용하는 클라이언트를 만들기 위해 Visual Studio에서 Win32
Console Application으로 프로젝트를 만들어보자. 프로젝트의 이름은 “ImplicitLinking”이라
고 하고, Console application, Empty project로 설정한다. 그리고 MyDLL.lib 파일과
MyDLL.DLL 파일을 이 console application 디렉토리로 복사한다. MyDLL.lib 파일은 EXE
파일을 빌드할 때 필요하며, MyDLL.DLL은 이 EXE 파일을 실행할 때 필요하다.
프로젝트가 생성되면 MyDLL.h 파일을 추가하고 다음과 같이 입력한다.
// 전역 변수 import.
extern "C" __declspec(dllimport) char *str;
// 전역 함수 import.
extern "C" __declspec(dllimport) int Add(int a, int b);
// 클래스 import.

class __declspec(dllimport) CCalc {
public:
int Add(int a, int b);
};
또한, main.cpp 파일을 추가하고, 다음과 같이 코드를 입력한다.
#include <stdio.h>
#include "MyDLL.h"
#pragma comment(lib, "MyDLL.lib")
int main(void) {
// import된 전역 변수 출력.
printf("%s\n", str);
// import된 전역 함수 테스트.
printf("2 + 3 = %d\n", Add(2, 3));
// import된 클래스 테스트.
CCalc Calc = CCalc();
printf("1 + 2 = %d\n", Calc.Add(1, 2));
return 0;
}
링커에게 MyDLL.lib 파일을 같이 링크하라고 알려주기 위해 #pragma comment(lib,
“MyDLL.lib”) 라는 지시어를 넣었다. 물론 이와 같이 하지 않고, 다음 그림과 같이 프로젝
트 설정에 들어가서 라이브러리 쓰는 곳에 MyDLL.lib를 써줘도 효과는 동일하다.

MyDLL.h 헤더 파일에서는 DLL에서 import할 변수, 함수, 클래스들을 써주는데,
__declspec(dllexport) 대신 __declspec(dllimport)를 쓴 것 외에는 별다른 차이점이 없다.
CCalc의 인스턴스를 생성 및 파괴하기 위한 CreateCalcInstance()와 DeleteCalcInstance()
는 묵시적인 DLL 링킹을 사용할 때에는 굳이 필요가 없으므로 아얘 함수 선언 자체를 빼
버렸다.
일단 이렇게 적절한 헤더 파일과 .LIB 파일을 링크하도록 설정한 후에는 이 변수, 함수,
클래스를 평상시와 똑같이 사용하면 된다. main.cpp에서 import된 변수, 함수, 클래스를 사
용하는 예를 보였는데, DLL에서 import했다고 해서 특별한 점은 하나도 없다.
import된 전역 변수와 전역 함수의 사용은 너무나 쉬워서 더 이상 설명할 것이 없다. 그
러나 CCalc 클래스의 경우에는 멤버 함수의 이름이 mangling이 되었음에도 왜 아무 문제
없이 작동이 되는가 잠시 생각해볼 필요가 있다. 그것은 DLL을 만들 때 사용했던 컴파일러
와 똑같은 컴파일러를 사용했기 때문이다. MyDLL.DLL을 import하는 EXE 파일 측에서도
import하는 심볼들에 대해 extern “C”가 적혀 있지 않다면 name mangling을 하는데, 동일
한 컴파일러를 썼으므로, name mangling이 동일하게 일어나기 때문이다. 즉, CCald::Add()
함수의 name mangling이 DLL과 EXE 파일에서 동일하게 적용되었기 때문이다.
묵시적으로 DLL을 링킹하는 EXE 파일에는 어떤 DLL 파일로부터 어떤 심볼들을 import
하는지에 대한 정보를 가지고 있는데, 이 정보 역시 DUMPBIN.EXE 유틸리티로 확인 가능

하다. 다음과 같이 이 EXE 파일의 import 정보를 확인해 보자.
C:\Programming\ImplicitLinking\Debug>dumpbin /imports ImplicitLinking.exe
Microsoft (R) COFF/PE Dumper Version 8.00.50215.44
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file ImplicitLinking.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
MyDLL.dll
4172F0 Import Address Table
417188 Import Name Table
0 time date stamp
0 Index of first forwarder reference
5 str
2 Add
1
?Add@CCalc@@QAEHHH@Z
MSVCR80D.dll
KERNEL32.dll
Summary
1000 .data
1000 .idata
2000 .rdata
1000 .rsrc
3000 .text
10000 .textbss
C:\Programming\ImplicitLinking\Debug>


위의 출력 결과는 너무 길어서 불필요한 부분은 삭제한 것이다.
위에서 보듯이 이 EXE 파일은 실행을 위해 3개의 DLL 파일을 필요로 한다. 즉,
MyDLL.DLL, MSVCR80D.DLL, KERNEL32.DLL를 필요로 한다. MSVCR80D.DLL은 C 런
타임 라이브러리에 대한 DLL(디버그 버전)이고, KERNEL32.DLL은 Win32의 기본적인 API
를 담고 있는 DLL이다. 그리고 우리가 만든 MyDLL.DLL에서 3개의 심볼을 import하고 있
는데, str과 Add()는 name mangling이 되지 않았으나, CCalc::Add()는 name mangling이
되어 있다. 그러나, DLL에서도 어차피 이것과 똑같은 이름으로 name mangling이 되어 있
기 때문에 아무 문제없이 작동이 되는 것이다.
정리하면, 묵시적 DLL 링킹을 사용하는 경우에, 동일한 컴파일러를 사용하기만 한다면,
DLL 안에 클래스를 넣는 것도 아무런 문제가 없다. 그러나 서로 다른 컴파일러를 사용한다
면 DLL에서 export된 클래스가 정상적으로 import된다는 보장이 없다. 그 이유는 심볼에
대한 name mangling에는 표준이 없고, 컴파일러 나름대로 아무렇게나 하기 때문이다. 애초
에 DLL이라는 것이 객체지향적 정보를 담을 수 있게 되어 있질 않아서 가지는 한계 정도라
고 보면 될 것 같다. 물론 최근의 .NET의 컴포넌트 역시 확장자는 DLL을 가지고 있지만,
이 DLL은 그 내부 형식이 우리가 지금 만든 DLL과는 조금 다르다. .NET의 DLL은 객체지
향적 정보를 담을 수 있도록 개선되어 있다.
위의 EXE 파일을 실행해 보면 예상했던 대로 다음과 같이 출력된다.
THE TRUTH IS OUT THERE
2 + 3 = 5
1 + 2 = 3
정상적으로 전역 변수, 전역 함수, 클래스를 import한 것을 볼 수 있다.
5. 명시적인 DLL 링킹
명시적인 DLL 링킹은 run-time 시에 필요로 하는 DLL을 메모리에 로딩하였다가 필요없
으면 메모리에서 삭제하는 식으로 사용하는 기법을 말한다. 상황에 따라 서로 다른 DLL을
로딩하여야 한다거나, 플러그인으로 설치된 DLL을 동적으로 읽어와서 메모리에 로딩해야
하는 경우에는 명시적일 DLL 링킹을 사용하여야 한다. 사용하기에는 명시적인 DLL 링킹이
조금 더 까다롭기는 하나, 그렇게 복잡하지는 않다. 오히려 DLL 로딩/언로딩에 대하여 더
강력한 컨트롤이 가능하다는 장점이 있다.
명시적인 DLL 링킹 방식에서는 EXE 파일의 compile-time에 헤더 파일과 .LIB 파일이
모두 필요 없다. 그리고 EXE 파일의 run-time에는 .DLL 파일만 있으면 된다. DLL 파일의

로딩 자체가 EXE 파일의 실행과 동시에 일어나는 것이 아니기 때문에, 필요로 하는 DLL이
존재하지 않는 경우에도 좀더 유연하게 대처할 수 있는 장점이 있다.
DLL을 실행시에 동적으로 로딩하기 위해서는 3가지 Win32 API가 사용된다.
LoadLibrary(), GetProcAddress(), FreeLibrary()가 그것이다. LoadLibrary()는 DLL을 메
모리에 로딩하기 위해 사용하며, FreeLibrary()는 더 이상 사용되지 않는 DLL을 메모리에
서 언로딩하기 위해 사용된다. GetProcAddress()는 DLL로부터 특정 심볼(변수나 함수)의
주소를 얻기 위해 사용된다. 자세한 함수의 설명은 MSDN을 참조하기 바란다.
그러면 실제로 이 명시적 DLL 링킹을 사용하는 클라이언트 프로그램을 만들어 보자.
Win32 프로젝트를 Console application, Empty project로 만들고, 이름은 ExplicitLinking
이라고 하자. 또한 MyDLL.DLL을 이 프로젝트의 디렉토리로 복사하자. MyDLL.LIB 파일은
명시적 링킹에서는 필요치 않다. 그리고 main.cpp라는 소스 파일을 추가하고, 다음과 같이
입력한다.
#include <Windows.h>
#include <stdio.h>
class CCalc {
public:
int Add(int a, int b);
};
int main(void) {
// MyDLL.DLL 로딩.
HMODULE hDLL = LoadLibrary("MyDLL.DLL");
if (hDLL == NULL) {
fprintf(stderr, "MyDLL.DLL을 로딩하는 데 실패했습니다.\n");
return 1;
}
// 전역변수 테스트.
char **str = (char **)GetProcAddress(hDLL, "str");
printf("str: %s\n", *str);
// 전역함수 테스트.
int (*pAdd)(int, int) = (int (*)(int, int))GetProcAddress(hDLL, "Add");

printf("2 + 3 = %d\n", pAdd(2, 3));
// 클래스 테스트.
CCalc *(*pCreateCalcInstance)(void) = (CCalc *(*)(void))GetProcAddress(hDLL,
"CreateCalcInstance");
void (*pDeleteCalcInstance)(CCalc *) = (void (*)(CCalc
*))GetProcAddress(hDLL, "DeleteCalcInstance");
int (__thiscall *pCalcAdd)(CCalc *, int, int) = (int (__thiscall *)(CCalc *,
int, int))GetProcAddress(hDLL, "
?Add@CCalc@@QAEHHH@Z");
CCalc *pCalc = pCreateCalcInstance();
printf("1 + 2 = %d\n", pCalcAdd(pCalc, 1, 2));
pDeleteCalcInstance(pCalc);
// MyDLL.DLL 언로딩.
FreeLibrary(hDLL);
return 0;
}
DLL을 로딩할 때에는 LoadLibrary() 함수를 사용하고, 언로딩할 때에는 FreeLibrary()를
사용한다. 로딩된 DLL은 HMODULE 타입의 변수에 핸들을 넣는데, HINSTANCE 타입을
사용해도 된다. HMODULE 타입과 HINSTANCE 타입은 서로 같게 사용된다.
DLL이 export하고 있는 특정 심볼의 주소를 얻을 때에는 GetProcAddress() 함수를 사
용한다. GetProcAddress() 함수의 첫번째 파라미터는 LoadLibrary()로 얻은 DLL의 핸들을
넣으면 되고, 두번째 파라미터는 로딩된 DLL로부터 찾을 심볼의 이름을 넣어주면 된다. 이
심볼의 이름은 DUMPBIN.EXE 유틸리티로 DLL 파일의 export 심볼을 조사할 때 나오는
심볼이어야 한다.
먼저 DLL의 전역 변수를 사용하는 방법부터 알아보자. export된 전역 변수의 심볼이
“str”이므로, 그 이름으로 GetProcAddress()를 호출하면 된다. 그리고 char **형에 포인터
를 넣었다. char *가 아니고 char **인 이유는 이미 잘 알고 있을 것이다. str 문자열 자체가
char *형인데, 거기에 대한 주소를 다시 얻기 위해 GetProcAddress()를 사용했으니, char
**형이 된다. printf()로 출력할 때에는 dereference operator를 하나 붙여 *str으로 출력하
면 문자열이 출력된다.
그 다음은 전역 함수의 사용을 알아보자. 만약 함수 포인터에 대해 익숙하지 않다면 잘
이해가 되지 않을 수도 있다. DLL에서 함수를 불러다 쓰기 위해서는 GetProcAddress()의

반환 값을 알맞은 함수 포인터형으로 캐스팅한 후에, 함수 포인터에 대입해야 한다. 함수의
이름이 “Add”이므로 그 이름으로 GetProcAddress()를 호출하고, 원래 Add() 함수와 똑 같
은 프로토타입의 함수 포인터에 그 주소를 대입시켰다. 그리고 함수 포인터를 이용하여
Add() 함수를 호출하였다. 참고로 pAdd가 함수 포인터라고 할 때, pAdd(2, 3)과 같이 사용
해도 되고, (*pAdd)(2, 3)과 같이 사용해도 된다. 프로그래밍 책에 따라 둘 중에 한가지 표
기법을 사용하는데, 둘 다 맞다. C 언어 표준에서도 두 가지 형태의 함수 포인터 사용을 모
두 합법적으로 본다.
그 다음 클래스의 사용은 조금 복잡하다. DLL의 명시적 링킹으로 클래스를 사용하기 위
해서는, 그 클래스 내에 인스턴스를 생성하는 함수와 파괴하는 함수를 만들어 두어야 한다.
그래서 CreateCalcInstance()와 DeleteCalcInstance() 함수를 만들어 둔 것이다. 이 2개의
함수는 CCalc 클래스의 인스턴스를 생성 및 파괴하는 역할을 담당하는데, 사용법 자체는
위에서 설명한 Add() 함수와 동일하다. 알맞은 함수 포인터에 함수의 주소를 넣고, 호출하
면 된다.
문제는 CCalc::Add() 함수인데, 앞에서도 이야기했듯이 전통적인 DLL 자체에는 객체지향
적 클래스의 정보를 담을 수 없기 때문에 클래스의 멤버 함수는 name mangling이 되어 하
나의 함수 형태로 export되게 된다. Add() 멤버 함수의 사용 방법을 이해하기 위해서는,
C++에서 클래스를 어떻게 내부적으로 구현하는가에 대해 이해를 해야 한다.
C++ 언어는 Bjarne Stroustrup라는 사람이 개발한 언어이다. 이 사람이 처음에 C++
언어를 개발하고, 실제 사용 가능한 컴파일러를 만들었는데, 그 컴파일러는 C++ 코드를 C
소스로 바꾸어주는 식으로 작동했다. 일단 C++ 코드를 C 코드로 바꾸고 나면, 다시 기존
의 C 컴파일러를 이용하여 기계어로 번역을 하는 식이었다. C++의 멤버 함수를 C 함수 형
식으로 바꿀 때에는, 함수의 첫번째 파라미터로 그 인스턴스를 넘겨주는 식으로 고쳐지게
된다. 예를 들어, CCalc::Add(int a, int b)라는 멤버 함수를 C 함수 형식으로 바꾼다면
Add(CCalc *thisptr, int a, int b)과 같은 식이 된다는 것이다. 그리고 함수 내에서 멤버 변
수에 접근하는 코드는 모두 thisptr->를 앞에 붙이게 된다. 이러한 아이디어는 Bjarne
Stroustrup가 처음 C++ 컴파일러를 개발할 때 사용했을 뿐만 아니라, 현재의 컴파일러에
서도 마찬가지이다. 우리가 CCalc 클래스를 DLL 안에 집어넣었는데, CCalc::Add() 함수도
이처럼 CCalc 인스턴스에 대한 포인터가 첫번째 파라미터로 넘겨지도록 묵시적으로 변경이
이루어진 것이다.
위의 예제 코드를 다시 한번 보면, CCalc::Add() 함수를 사용하기 위한 함수 포인터를 int
(__thiscall *pCalcAdd)(CCalc *, int, int)와 같이 선언하였다. 방금 설명한 것처럼 CCalc *
형을 이 함수의 첫번째 파라미터로 넣었다. 그리고 또 하나 __thiscall이라는 키워드가 붙어
있는데, 이것은 calling convention을 나타내는 것이다. 이 경우처럼 원래 클래스 멤버 함수
였던 함수의 경우, 첫번째 파라미터로 인스턴스의 포인터를 넘기는 것 외에, 그 인스턴스의
How DLL Works 15
포인터를 ECX 레지스터에도 복사하라는 의미의 calling convention이 __thiscall이다. 참고
로 많이 사용되는 calling convention에는 __stdcall, __cdecl 등이 있다. __stdcall은 파스칼
방식의 calling convention으로 Win32 API들이 그러한 calling convention을 사용한다.
__cdecl(C declaration)은 우리가 만든 함수에 적용되는 디폴트 calling convention이다.
Calling convention에 따라 함수의 파라미터가 스택에 push되는 순서, 이 파라미터들을 누
가 해제하는가(caller 혹은 callee) 등이 달라진다.
CCald::Add() 함수는 name mangling이 되었기 때문에, 직접 DUMPBIN.EXE 유틸리티로
export된 정확한 함수 이름을 알아내서, 그 이름으로 GetProcAddress()를 호출하는 수밖
에 없다. 그래서 여기서도
?Add@CCalc@@QAEHHH@Z와 같은 이상한 이름으로 함수의
주소를 찾아냈다.
명시적 링킹 방식으로 클래스를 사용하기 위해서는 조금 복잡한 절차가 있는 것은 사실이
다. 그러나 분명히 불가능하지는 않다. 이러한 피곤한 과정을 거친다면 사실 DLL 개발자와
EXE 개발자가 서로 컴파일러가 달라도 그 DLL을 로딩해서 export된 클래스를 사용할 수
는 있다.

6. 결론
지금까지 기초적인 DLL 사용법에 대해 알아보았다. 이제, DLL 안에 전역 변수, 전역 함
수, 클래스를 넣어두고 사용할 수 있을 것이다. 또한 순수한 C/C++만을 사용한 DLL에서
도 클래스를 export할 수 있다는 것을 알 수 있을 것이다.
그리고 다른 사람이 개발한 DLL을 사용하려고 하는데, 자꾸 링크 에러가 난다거나
GetProcAddress()가 올바른 함수의 주소를 찾지 못할 때, DUMPBIN.EXE 유틸리티를 이용
하여 제대로 함수가 export되기는 했는지, 혹시 엉뚱한 이름으로 name mangling이 되지는
않았는지 조사해 볼 수도 있을 것이다. C++ 컴파일러에서 name mangling을 왜 하는지, 또
그걸 막기 위해 extern “C”를 사용한다는 것도 이해할 수 있을 것이다.
그러나 이것이 DLL의 전부는 아니다. DLL에 대하여 더 깊이 알고자 하는 사람이 있다면
각종 MSDN 문서와 Programming Applications for MS Windows(Jeffrey) 책을 읽어보기
바란다. 특히 메모리와 관련된 이슈에 대해 잘 알아두면 도움이 될 것이다.
Trackback Address :: http://joyholic.kr/trackback/132 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/11 11:34

DLL 모듈 제작

<<< 학습목표 >>>

- DLL의 개념 파악

- DLL의 종류 학습

- DLL의 생성 및 사용

- DLL프로그램을 어플리케이션 프로그램에서 활용하기

1. DLL을 생성하고 사용하기

1.1 DLL에 대해서

동적 링크 라이브러리(DLL : Dynamic Link Library)는 초기의 윈도우 운영체제 시절 마이크로소프트에 의해 사용되었다. DLL은 다른 애플리케이션에서 사용할 수 있도록 묶은 함수 꾸러미라는 점에서 라이브러리 모듈과 유사하다. 차이점이라고 한다면 애플리케이션이 이 라이브러리에 링크 할 때라고 할 수 있다. 라이브러리 모듈(LIB)의 경우 애플리케이션의 빌드(컴파일하고 링크 하는 것) 과정에서 링크 되며, 따라서 라이브러리 파일에 들어 있는 코드는 애플리케이션의 실행 파일의 일부가 된다. DLL의 경우 애플리케이션에 의해 참조되고 호출되고 호출되는 독립된 파일로 남아 있다.

라이브러리 모듈 대신 DLL을 만드는 이유는 몇 가지가 있다. 첫 번째, 애플리케이션 실행 파일의 크기를 줄일 수 있다. 여럿 애플리케이션 사이에서 공통적으로 사용되는 함수를 DLL에 모아 놓고 공유하게 하기 때문이다. 또한, DLL의 함수를 개선해야 할 때 애플리케이션 실행 파일을 고칠 필요 없이 DLL만 고치면 된다.(DLL이 익스포트(export : 개방)하고 있는 함수 목록, 인터페이스가 변하지 않는다는 가정 하에). 마지막으로, DLL은 비주얼 C++이외의 다른 윈도우 프로그래밍 언어에서도 사용할 수 있어서, 여러분이 어쩌다가 만들게 될 상업용 라이브러리를 더 넓은 시장에서 공급할 수 있다는 돈벌이 측면의 장점도 가지고 있다.

DLL이 다른 애플리케이션에서 사용될 수 있는 컴파일 된 코드를 가진 라이브러리 파일이라는 점은 자명하다. DLL은 익스포트(export)라는 동작을 통해 몇 가지의 함수와 클래스를 사용할 수 있도록 개방한다. 어떤 함수가 익스포트되면, 이 함수는 DLL에 포함되는 어떤 테이블에 추가된다. 이 테이블에는 해당 DLL에 속해 있는 모든 익스포트 함수의 코드 주소상 취치가 적혀 있고, 이것은 각각의 함수 호출에 직접적으로 쓰인다. 익스포트되지 않고 이 테이블에 추가되지 않은 함수라면 다른 애플리케이션 또는 DLL에 의해 액세스되거나 호출되는 일이 절대로 없을 것이다.

1.2 DLL의 종류

비주얼 C++로 간단하게 만들 수 있는 DLL에는 두 가지가 있는데, 하나는 MFC 확장 DLL(MFC extension DLL)이며, 또 하나는 정규 DLL(regular DLL)이다.

1) MFC 확장 DLL

코딩이 간단하고 만들기 쉬운 DLL이다. 단지 다른 클래스 꾸러미의 일종으로 간주하고 사용하면 되기 때문이다. 이 DLL로부터 익스포트 할 클래스에 대해서 여러분이 해줄 일은 클래스 선언문에 AFX_EXT_CLASS란 매크로만 붙여주는 것뿐이다. 다음을 보도록 하자.

class AFX_EXT_CLASS CMyClass

{

·

·

·

};

즉, 이 클래스를 다른 비주얼 C++ 애플리케이션에 의해 사용될 수 있도록 익스포트하겠다는 뜻이다. 이 클래스를 가지고 있는 DLL에 링크될 애플리케이션에 의해 사용되는 헤더 파일에 이 매크로를 반드시 포함시켜야 클래스를 제대로 임포트 할 수 있다.

MFC 확장 DLL의 단점이라면 MFC를 지원하지 않는 프로그래밍 언어에서 사용할 수가 없다는 점이다. 볼랜드나 시맨텍의 C++ 컴파일러처럼 MFC를 지원하는 C++ 컴파일러에서만 사용할 수 있다.

2) 정규 DLL

이 DLL은 C++ 클래스가 아니라 표준 함수를 익스포트한다. 따라서, MFC 확장 DLL을 만들 때보다 조금 더 신경을 써야 한다. 즉, DLL 내부에서는 원하는 클래스를 모두 사용할 수 있지만 다른 애플리케이션이 사용하도록 하기 위해서는 표준 함수 형식을 호출할 수 있도록 해야 한다.

함수를 익스포트하려면 함수 이름 앞에 다음과 같이 선언해서 익스포트 함수라는 것을 선언한다.

extern "C" <function type> PASCAL EXPORT <function declaration>

이것은 헤더 파일과 실제 소스 파일 모두에 넣어주어야 한다. extern "C" 부분은 현재의 함수 선언이 표준 C 함수라는 것을 명시함으로써 C++의 이름 만들기(네임 맹글링 : Name Mangling) 가 작동하지 않도록 한다. PASCAL은 컴파일러에게 함수의 호출 형식이 PASCAL 형이라는 것을 알리는 부분으로, 인수 전달은 왼쪽에서 오른쪽으로, 스택 청소는 호출된 함수가 하도록 한다. 마지막으로, EXPORT 부분은 컴파일러에게 현재의 함수가 DLL에서 익스포트 되며, DLL의 외부에서 호출될 수 있다는 것을 알려주는 것이다.

정규 DLL에서 함수를 익스포트 하기 위해 해주어야 하는 또 한 가지는 익스포트되는 모든 함수의 이름을 모듈 정의 파일이라고 불리는 DEF 파일에 모아 놓는 일인데, 이 파일은 스텁 LIB 파일과 익스포트 테이블을 만드는데 쓰인다. DEF 파일은 DLL, 즉 라이브러리의 이름, DLL의 간략한 설명, 익스포트될 모든 함수의 이름을 담고 있으며, 일정한 포맷을 따라서 만들어야 하기 때문에 DLL 위저드에 의해 만들어진 디폴트 DEF 파일은 함수 이름을 추가하는 것 이외의 수정을 가하지 말도록 한다. 다음은 정형적인 DEF 파일의 내용이다.

LIBRARY "mydll"

DESCRIPTION 'mydll Windows Dynamic Link Library'

EXPORTS

; Explicit exports can go here

MyFunc1

MyFunc2

만일 정규 DLL에 MFC 클래스를 사용하고 싶으면, 익스포트되는 모든 함수 코드의 첫 번째 줄에서 AFX_MANAGE_STATE 매크로를 호출해야 한다. 이 매크로는 익스포트되는 함수들이 쓰레드 간에 보호받을 수 있도록 하여, 이 클래스 함수가 두 개 이상의 프로그램(쓰레드)에 의해 동시에 호출될 수 잇게 된다. AFX_MANAGE_STATE 매크로는 하나의 인수, 즉 AFX_MODULE_STATE 구조체의 포인터를 인수로 받는데, 이 포인터는 AfxGetStaticModuleState() 함수로 얻을 수 있다. 따라서, MFC 클래스를 사용한 익스포트 함수는 다음과 같은 형식을 띤다.

extern "C" void PASCAL EXPORT MyFunc( . . .)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

}

1.3 결과화면.

그림 8.7 확장 DLL결과

그림 8.8 정규 DLL결과

2. 확장 DLL 구현과정

※NOTE - DLL 설계시에는

DLL을 만들 때는 동시에 실행되는 여러 애플리케이션들이 해당 DLL에 들어 있는 함수들이 사용된다는 사실에 염두를 두어야 한다. 결과적으로 DLL에 포함되는 모든 함수는 쓰레드 간에 보호를 받아야 한다.

각각의 함수의 범위에 있지 않은 모든 변수들은 DLL이 아니라 애플리케이션에 의해 유지되도록 해야 하며, DLL에 의해 사용되는 모든 애플리케이션 변수는 함수의 인수로서 DLL로 넘겨져야 한다. DLL 내부에서 사용되는 전역 변수는 어떤 함수가 실행되는 동안 애플리케이션 프로세스가 가지고 있는 변수로 바뀌어서 예상하지 못한 결과를 낳을 수도 있다.

2.1 MFC 확장 DLL을 만들자.

1) MFC로 DLL을 만드는 새 프로젝트를 시작한다.

그림8.9

MFC DLL

위저드를 선택

그림 8.10

DLL타입의 설정

2) 선긋기와 그리기에 관련된 클래스의 소스 코드와 헤더파일을 모두 프로젝트 디렉토 리에 복사한다.(앞의 라이브러리 프로젝트에서 방금 만든 프로젝트 디렉토리로 line.cpp, line.h, ModArt.cpp, ModArt.h)를 복사한다)

3) 복사해온 그리기 클래스에 AFX_EXT_CLASS 매크로를 추가한다.

(색깔 테이블의 변수를 지운다.)

소스 8.16 CModArt 클래스

class AFX_EXT_CLASS CModArt : public CObject

{

public:

// static const COLORREF m_crColors[8]; 색깔 테이블의 변수를 지운다

void NewDrawing();

virtual void Serialize(CArchive &ar);

void Draw(CDC *pDC);

void ClearDrawing();

void SetRect(CRect rDrawArea);

CModArt();

virtual ~CModArt();

private:

void NewLine();

CRect m_rDrawArea;

CObArray m_oaLines;

};

4) 전역 정적 변수로 되어 있는 색깔 테이블을 NewLine() 함수 안의 지역 변수로 만든 다.

소스 8.17 NewLine() 함수

void CModArt::NewLine()

{

int lNumLines;

int lCurLine;

int nCurColor;

UINT nCurWidth;

CPoint pTo;

CPoint pFrom;

//사각 영역의 왼쪽 상단보다 오른쪽 하단의 좌표가 크게 만든다.

m_rDrawArea.NormalizeRect();

//사각 영역의 폭과 높이를 구한다.

int lWidth = m_rDrawArea.Width();

int lHeight = m_rDrawArea.Height();

COLORREF crColors[8] = {

RGB( 0, 0, 0), // 검정

RGB( 0, 0, 255), // 파랑

RGB( 0, 255, 0), // 녹색

RGB( 0, 255, 255), // 시안

RGB( 255, 0, 0), // 빨강

RGB( 255, 0, 255), // 자주색

RGB( 255, 255, 0), // 노랑색

RGB( 255, 255, 255) // 흰색

};

// 난수발생기로 불규칙 곡선의 수를 결정한다.

lNumLines = rand() % 200;

// 결정한 개수가 0보다 커야하는 조건

if (lNumLines > 0)

{

// 난수 발생기로 색깔 결정

nCurColor = rand() % 8;

// 난수 발생기로 펜의 두께를 결정

nCurWidth = (rand() % 8) + 1;

// 불규칙 곡선의 시작점을 결정

pFrom.x = (rand() % lWidth) + m_rDrawArea.left;

pFrom.y = (rand() % lHeight) + m_rDrawArea.top;

// 불규칙 곡선 수만큼 루프

for (lCurLine = 0; lCurLine < lNumLines; lCurLine++)

{

// 각 선의 끝점을 결정한다.

pTo.x = ((rand() % 20) - 10) + pFrom.x;

pTo.y = ((rand() % 20) - 10) + pFrom.y;

// CLine roc를 새로 생성한다.

CLine *pLine = new CLine(pFrom, pTo, nCurWidth, crColors[nCurColor]);

try

{

m_oaLines.Add(pLine); 선긋기 개체를 추가한다.

}

// 메모리 예외

catch (CMemoryException* perr)

{

// 예외 발생시에는 발생을 메시지 박스를 통해 보인다. AfxMessageBox("Out of memory",

MB_ICONSTOP | MB_OK);

if (pLine)

{

// 선긋기 개체의 생성시에는 꼭 삭제해야 한다.

delete pLine;

pLine = NULL;

}

// 예외 개체도 삭제한다.

perr->Delete();

}

// 끝점을 시작점으로 넘겨준다.

pFrom = pTo;

}

}

}

5) DLL을 컴파일하고나서, 탐색기로 돌아가 프로젝트 디렉토리에 있는 debug 디렉토리 안에서 방금 만든 DLL을 찾아서 복사한다.

2.2 시험용 애플리케이션을 만들자.

(라이브러리 모듈 생성시에 만들었던 시험용 애플리케이션 이용시에는 LIB 파일을 삭제하고 그리기 클래스를 위해 인클루드 되었던 헤더 파일을 고친다.)

1) 표준 SDI 혹은 MDI 애플리케이션 골격을 만든다. 이름은 TestApp2로 하면 좋겠고, 네번째 단계에서 Advanced 버튼을 클릭해서 파일 확장자도 설정해 주도록 하자.

2) 프로젝트의 메인 메뉴에서 Project | Add to Project | Piles를 선택하고 파일형식에 서 Library Files(.lib)를 선택하고 DLL프로젝트의 debug 디렉토리로 옮겨가서 방금 만들었던 DLL과 함께 생성된 LIB 파일을 선택하고 추가한다.

3) 그리기 클래스를 위해 인클루드 되었던 헤더 파일을 고친다.

소스 8.18 CTestAppDoc 소스 파일

#include "stdafx.h"

#include "TestApp2.h"

#include "..\ModArtDll\ModArt.h"

#include "TestApp2Doc.h"

2.3 기능을 강화시킨 DLL.

그려지는 불규칙한 선의 개수를 증가시키고, 선의 길이를 늘리고 어떠한 색깔이라도 선택해서 사용할 수 있도록 한다.

소스 8.19 NewLine() 함수

void CModArt::NewLine()

{

int lNumLines;

int lCurLine;

int nCurColor;

UINT nCurWidth;

CPoint pTo;

CPoint pFrom;

int cRed;

int cBlue;

int cGreen;

//사각 영역의 왼쪽 상단보다 오른쪽 하단의 좌표가 크게 만든다.

m_rDrawArea.NormalizeRect();

//사각 영역의 폭과 높이를 구한다.

int lWidth = m_rDrawArea.Width();

int lHeight = m_rDrawArea.Height();

/*

COLORREF crColors[8] = {

RGB( 0, 0, 0), // 검정

RGB( 0, 0, 255), // 파랑

RGB( 0, 255, 0), // 녹색

RGB( 0, 255, 255), // 시안

RGB( 255, 0, 0), // 빨강

RGB( 255, 0, 255), // 자주색

RGB( 255, 255, 0), // 노랑색

RGB( 255, 255, 255) // 흰색

};

*/

// 난수발생기로 불규칙 곡선의 수를 결정한다.

lNumLines = rand() % 200;

// 결정한 개수가 0보다 커야하는 조건

if (lNumLines > 0)

{

cRed = rand() % 255;

cBlue = rand() % 255;

cGreen = rand() % 255;

// 난수 발생기로 펜의 두께를 결정

nCurWidth = (rand() % 8) + 1;

// 불규칙 곡선의 시작점을 결정

pFrom.x = (rand() % lWidth) + m_rDrawArea.left;

pFrom.y = (rand() % lHeight) + m_rDrawArea.top;

// 불규칙 곡선 수만큼 루프

for (lCurLine = 0; lCurLine < lNumLines; lCurLine++)

{

// 각 선의 끝점을 결정한다.

pTo.x = ((rand() % 20) - 10) + pFrom.x;

pTo.y = ((rand() % 20) - 10) + pFrom.y;

// CLine 개체를 새로 생성한다.

CLine *pLine = new CLine(pFrom, pTo, nCurWidth, RGB(cRed, cGreen, cBlue));

try

{

m_oaLines.Add(pLine); //선긋기 개체를 추가한다.

}

// 메모리 예외

catch (CMemoryException* perr)

{

// 예외 발생시에는 발생을 메시지 박스를 통해 보인다.

AfxMessageBox("Out of memory",

MB_ICONSTOP | MB_OK);

if (pLine)

{

// 선긋기 개체의 생성시에는 꼭 삭제해야 한다.

delete pLine;

pLine = NULL;

}

// 예외 개체도 삭제한다.

perr->Delete();

}

// 끝점을 시작점으로 넘겨준다.

pFrom = pTo;

}

}

}

변경을 하였으므로 이 DLL을 다시 컴파일하고 컴파일된 DLL을 시험용 애플리케이 션의 debug 디렉토리로 복사하고 시험용 애플리케이션을 실행하자.

3. 확장 DLL 구현과정.

※NOTE - DLL 설계시에는

MFC 확장 DLL을 만들고 사용하고 있는 여러분 자신은 어쩌면 애플리케이션이 소유하고 있지 않은 변수를 DLL 안에서 사용하는 것에 대한 규칙을 깨고 있다고 생각을 하고 있을지도 모르겠지만, 실제로는 그렇지 않다. 그리기 클래스의 인스턴스는 도큐먼트 클래스의 멤버이다. 애플리케이션에 의해 생성되고 유지되었으며, DLL은 결코 관여하지 않았다.

MFC 확장 DLL을 정규 DLL로 바꾸기 위해서는 그리기 클래스 자체를 표준 함수로 바꿔주는 일을 해야 한다. 이 과정에서 개체 배열은 애플리케이션의 도큐먼트 클래스의 멤버 변수가 되어야 하며, 익스포트되는 DLL의 모든 함수들에게 인수로 넘겨져야 한다.

1) MFC로 DLL을 만드는 새 프로젝트를 시작한다.

MFC DLL 위저드를 선택하고 DLL타입을 설정한다.(디폴트 설정으로 되어있다.)

그림8.11 DLL타입설정

2) 선긋기와 그리기에 관련된 클래스의 소스 코드와 헤더파일을 모두 프로젝트 디렉토리 에 복사한다.(앞의 라이브러리 프로젝트에서 방금 만든 프로젝트 디렉토리로 line.cpp, line.h, ModArt.cpp, ModArt.h)를 복사한다)

3) 헤더 파일을 고치자.

그리기 클래스 헤더 파일을 열고, 클래스 선언문 자체를 함수 원형 선언문으로 완전히 바꾸어 버리자.

소스 8.20 ModArt 헤더파일

extern"C"voidPASCALEXPORTModArtNewDrawing(CRectpRect, CObArray *poaLines);

extern "C" void PASCAL EXPORT ModArtSerialize(CArchive &ar,

CObArray *poaLines);

extern "C" void PASCAL EXPORT ModArtDraw(CDC *pDC, CObArray *poaLines);

extern "C" void PASCAL EXPORT ModArtClearDrawing(CObArray *poaLines);

void NewLine(CRect pRect, CObArray *poaLines);

4) 그림을 만들어 내는 함수를 고치자.

소스 8.21 NewDrawing() 함수

extern "C" void PASCAL EXPORT ModArtNewDrawing(CRect pRect,

CObArray *poaLines)

//그리는 영역을 얻어내기 위한 CRect 개체와 개체 배열의 포인터를 인수로 넘겨준다.

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// 함수 몸체가 여기에 들어감

int lNumLines;

int lCurLine;

// 난수 발생기를 초기화 시켜준다.

srand((unsigned)time(NULL));

// 난수 발생기를 통해서 선의 개수를 결정한다.

lNumLines = rand() % 50;

// 선의 수가 0이상이면

if (lNumLines > 0)

{

// 선의 개수만큼 루프를 돈다.

for (lCurLine = 0; lCurLine < lNumLines; lCurLine++)

{

//새 선을 생성한다.

NewLine(pRect, poaLines);

}

}

}

소스 8.22 NewLine() 함수

void CModArt::NewLine(CRect pRect, CObArray *poaLines)

{ //CRect 개체와 개체 배열이 함수의 인수로 전달되도록 한다.

int lNumLines;

int lCurLine;

// int nCurColor;

UINT nCurWidth;

CPoint pTo;

CPoint pFrom;

int cRed;

int cBlue;

int cGreen;

//사각 영역의 왼쪽 상단보다 오른쪽 하단의 좌표가 크게 만든다.

m_rDrawArea.NormalizeRect();

//사각 영역의 폭과 높이를 구한다.

int lWidth = m_rDrawArea.Width();

int lHeight = m_rDrawArea.Height();

/*

COLORREF crColors[8] = {

RGB( 0, 0, 0), // 검정

RGB( 0, 0, 255), // 파랑

RGB( 0, 255, 0), // 녹색

RGB( 0, 255, 255), // 시안

RGB( 255, 0, 0), // 빨강

RGB( 255, 0, 255), // 자주색

RGB( 255, 255, 0), // 노랑색

RGB( 255, 255, 255) // 흰색

};

*/

// 난수발생기로 불규칙 곡선의 수를 결정한다.

lNumLines = rand() % 200;

// 결정한 개수가 0보다 커야하는 조건

if (lNumLines > 0)

{

cRed = rand() % 255;

cBlue = rand() % 255;

cGreen = rand() % 255;

// 난수 발생기로 펜의 두께를 결정

nCurWidth = (rand() % 8) + 1;

// 불규칙 곡선의 시작점을 결정

pFrom.x = (rand() % lWidth) + pRect.left;

pFrom.y = (rand() % lHeight) + pRect.top;

// 불규칙 곡선 수만큼 루프

for (lCurLine = 0; lCurLine < lNumLines; lCurLine++)

{

// 각 선의 끝점을 결정한다.

pTo.x = ((rand() % 20) - 10) + pFrom.x;

pTo.y = ((rand() % 20) - 10) + pFrom.y;

// CLine 개체를 새로 생성한다.

CLine *pLine = new CLine(pFrom, pTo, nCurWidth, RGB(cRed, cGreen, cBlue));

try

{

poaLines->Add(pLine); 선긋기 개체를 추가한다.

}

// 메모리 예외

catch (CMemoryException* perr)

{

// 예외 발생시에는 발생을 메시지 박스를 통해 보인다.

AfxMessageBox("Out of memory",

MB_ICONSTOP | MB_OK);

if (pLine)

{

// 선긋기 개체의 생성시에는 꼭 삭제해야 한다.

delete pLine;

pLine = NULL;

}

// 예외 개체도 삭제한다.

perr->Delete();

}

// 끝점을 시작점으로 넘겨준다.

pFrom = pTo;

}

}

}

소스 8.23 ModArtDraw() 함수

extern "C" void PASCAL EXPORT ModArtDraw(CDC *pDC, CObArray *poaLines)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

//함수 몸체가 시작됨

// 개체 배열 내의 선의 개수를 알아낸다.

int liCount = poaLines->GetSize();

int liPos;

if (liCount)

{

for (liPos = 0; liPos < liCount; liPos++)

((CLine*)(*poaLines)[liPos])->Draw(pDC);

}

}

소스 8.24 ModArtSerialize() 함수

extern "C" void PASCAL EXPORT ModArtSerialize(CArchive &ar, CObArray *poaLines)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

//함수 몸체가 시작됨

// 아카이브 개체를 배열로 넘긴다.

poaLines->Serialize(ar);

}

소스 8.25 ModArtClearDrawing() 함수

extern "C" void PASCAL EXPORT ModArtClearDrawing(CObArray *poaLines)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

// 함수의 몸체가 시작됨

// 개체 배열 내의 선의 개수를 알아낸다

int liCount = poaLines->GetSize();

int liPos;

if (liCount)

{

// 배열 내부의 각 개체를 삭제한다.

for (liPos = 0; liPos < liCount; liPos++)

delete (*poaLines)[liPos];

//배열을 리셋한다.

poaLines->RemoveAll();

}

}

5) 모듈 정의 파일을 만들자.

현재 만들고 있는 DLL을 컴파일하기 전에, 모듈 정의 파일에다가 모든 함수의 이름을 추가시켜 두어야 한다.

소스 8.26 ModArtRDll.def 파일

; ModArtRDll.def : Declares the module parameters for the DLL.

LIBRARY "ModArtRDll"

DESCRIPTION 'ModArtRDll Windows Dynamic Link Library'

EXPORTS

; Explicit exports can go here

ModArtNewDrawing

ModArtSerialize

ModArtDraw

ModArtClearDrawing

6) 정규 DLL을 컴파일하고 시험용 애플리케이션의 debug 디렉토리에 복사하도록 하자.

7) 시험용 애플리케이션이 정규 DLL을 사용할 수 있도록 고치자.

(1) 이제 시험용 애플리케이션 프로젝트에서 LIB 파일을 없애는 것부터 작업을 시작하 기로 하자. 파일뷰를 사용해서 이전의 LIB 파일을 삭제한 다음, 메인 메뉴에서 Project | Add To Project | Files를 선택해서 새 DLL에 대한 LIB 파일을 추가한다.

(2) 도큐먼트와 뷰 클래스의 소스 코드를 고쳐서 새 DLL 프로젝트 디렉토리에 들어 있 는 헤더를 새로 인클루드하고, 애플리케이션 클래스의 소스 코드 파일에서 인클루드 문장을 없앤다. 그리기 클래스의 인스턴스를 생성하지 않을 것이기 때문에, 애플리케 이션 클래스의 파일은 이 DLL에 대해 신경 안쓰게 해도 된다.

(3) 도큐먼트 클래스의 헤더 파일을 열고, 도큐먼트 클래스 선언문을 편집하자. GetDrawing() 함수의 반환 타입을 바꾸어서 개체 배열의 포인터를 반환하게 하고, 그리기 클래스 타입으로 되어있는 멤버 변수 선언문을 없애는 대신 개체 배열을 멤버 변수로 넣자.

소스 8.27 CTestAppDoc의 헤더파일

class CTestApp2Doc : public CDocument

{

protected: // create from serialization only

CTestApp2Doc();

DECLARE_DYNCREATE(CTestApp2Doc)

// Attributes

.

.

.

// Implementation

public:

CObArray* GetDrawing();

virtual ~CTestApp2Doc();

#ifdef _DEBUG

.

.

.

private:

CObArray m_oaLines;

};

8) 도큐먼트 클래스의 멤버 함수를 고치자.

그리기 클래스 개체의 멤버 함수를 호출하는 부분이 모두 정규 DLL에 들어 있는(멤버 함수와 대응되는 동작을 하는) 함수를 호출하는 문장으로 바꾸자.

소스 8. 28 OnNewDocument() 함수

BOOL CTestApp2Doc::OnNewDocument()

{

if (!CDocument::OnNewDocument())

return FALSE;

// TODO: add reinitialization code here

// (SDI documents will reuse this document)

// 뷰의 위치를 얻어낸다.

POSITION pos = GetFirstViewPosition();

if (pos != NULL)

{

// Get a pointer to the view

CView* pView = GetNextView(pos);

RECT lWndRect;

// 표시할 영역의 사각형을 얻어낸다.

pView->GetClientRect(&lWndRect);

/* m_maDrawing.SetRect(lWndRect); 그리기클래스 개체에 CRect를

m_maDrawing.NewDrawing(); 넘겨준 함수호출문을 없앤다 */

ModArtNewDrawing(lWndRect, &m_oaLines);

//DLL이 들어있는 해당함수를 호출하도록한다.

}

return TRUE;

}

소스 8. 29 Serialize() 함수

void CTestApp2Doc::Serialize(CArchive& ar)

{

// DLL이 들어있는 직렬화 함수를 호출해서 직렬화한다.

ModArtSerialize(ar, &m_oaLines);

}

소스 8.30 DeleteContents() 함수

void CTestApp2Doc::DeleteContents()

{

// TODO: Add your specialized code here and/or call the base class

// DLL의 ModArtClearDrawing를 호출해서 그림을 지운다.

ModArtClearDrawing(&m_oaLines);

CDocument::DeleteContents();

}

소스 8.31 GetDrawing() 함수

CObArray* CTestApp2Doc::GetDrawing()

{

// 개체 배열 포인터(CObArray*)를 반환한다.

return &m_oaLines;

}

9) 뷰클래스의 함수를 고치자.

소스 8.32 OnDraw(CDC* pDC) 함수

void CTestApp2View::OnDraw(CDC* pDC)

{

CTestApp2Doc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// TODO: add draw code for native data here

// 개체 배열의 포인터를 얻어낸다.

CObArray* m_oaLines = pDoc->GetDrawing();

// DLL 함수인 ModArtDraw()를 이용해서 그림을 그린다.

ModArtDraw(pDC, m_oaLines);

}

10) 시험용 애플리케이션을 컴파일 한 다음 실행해보자.

<<< 요약 >>>

이번 장에서는 여러분이 만든 함수를 다른 프로그래머가 쓸 수 있도록 꾸러미로 만드는 두 가지 방법을 배웠다. MFC 확장 DLL의 경우 만들기가 매우 간단했고 비주얼 C++애플리케이션에서 사용하는 절차도 매우 쉬웠다. 이 DLL을 사용하는 애플리케이션의 재컴파일 없이 DLL을 수정하는 과정을 통해 DLL은 유지보수가 더 용이하다는 점도 확인하였다. 또한 여러분은 비주얼 C++가 아닌 다른 언어로 만들어진 애플리케이션에서 사용할 수 있는 정규 DLL이란 것을 만드는데 필요한 것들을 배웠는데, DLL에서 익스포트할 함수를 C 스타일의 표준 함수로 바꾸는데 유의해야 할 점과 이 DLL을 사용하는 애플리케이션에서 취해 주어야 하는 변경사항에 대해서 공부하였다.

<<< Q&A >>>

Q1: "주의"사항에 나온대로, 지금 만든 정규 DLL을 비주얼 C++가 아닌 다른 언어로 만들어진 애플리케이션에서 사용하게 하고 싶어요. 자세한 설명을 부탁드립니다.

A1: 우선 함수에 넘겨지는 모든 인수를 MFC 클래스가 아닌 구조체로 바꾸셔야 합니다. 예를 들면, ModArtNewDrawing() 함수의 경우 CRect 클래스 대신 RECT 구조체 타입으로 바꾸고, 개체 배열(CObArray 타입)의 포인터 대신 일반 포인터로 바꾸는 것이죠. 그 다음, 함수 내에서 적절한 데이터 변환과 형 변환(캐스팅)을 해주어야 합니다.

<소스>

또한, DLL에 개체 배열을 생성하는 함수를 추가해 주어야 합니다.(소스 ...참조). 애플리케이션에서는 생성된 개체 배열의 포인터를 일반 포인터의 형태로 가지고 있게 하고, 이것을 알아서 삭제하게 하는 것이죠.

<소스>

Q2: 제 DLL을 사용하는 애플리케이션을 재컴파일해야 할 때는 언제입니까?

A2: 익스포트되는 함수를 바꿀 때이죠. 함수를 추가한다든지, 인수를 없앤다든지 바꾼다든지 하는 일이 모두 여기에 속합니다. 만일 MFC 확장 DLL으로 작업하고 있었다면, 이 애플리케이션을 사용하는 애플리케이션은 익스포트되는 클래스의 public 멤버 함수가 변경되었든지, 함수나 변수가 새로 추가되거나 삭제되었다는지 했을 때 재컴파일되어야 하죠. 원래부터 변경된 함수를 사용하지 않는 애플리케이션이었다면 상관없습니다만, 그래도 매사에 확실히 하기 위해 재컴파일 해주는 것이 좋습니다.

<<< 연습문제 >>>

1. 선긋기(CLine) 클래스를 MFC 확장 DLL로 분리하고 이것을 정규 DLL과 함께 사용해 보자.

1) 새 프로젝트를 생성한다. 이 프로젝트는 AppWizard (DLL)로 시작하고, LineDll이란 이름을 붙이자.

2) 만들 DLL을 MFC 확장 DLL로 설정하고 나서 프로젝트 골격을 만든 다음, 선긋기 (Line) 소스 코드와 헤더 파일을 이 프로젝트 디렉토리에 복사하고, 이 프로젝트에 추 가한다.

3) CLine 클래스 선언문에 AFX_EXT_CLASS 매크로를 추가하여 클래스 선언문을 바 꾼다.

4) DLL을 재컴파일하고, 이 DLL을 시험용 애플리케이션에 대한 debug 디렉토리로 복사한다.

5) 정규 DLL 프로젝트를 연다. 프로젝트 워크스페이스의 파일 뷰에서 선긋기에 관한 소스 코드와 헤더를 이 프로젝트에서 삭제하고, 선긋기 DLL에 대한 LIB 파일을 프로젝트에 추가한다. 그리고 클래스의 헤더를 고쳐서 CLine DLL 프로젝트 디렉토리에 있는 것을 인클루드하자.

소스 8.33 ModArt의 소스파일

// ModArt.cpp: implementation of the CModArt class.

//

//////////////////////////////////////////////////////////////////////

#include <stdlib.h>

#include <time.h>

#include "stdafx.h"

#include "..\Line\Line.h"

#include "ModArt.h"

6) 프로젝트를 컴파일한다. 이 DLL을 시험용 애플리케이션 프로젝트의 debug 디렉토리 에 복사하고 테스트 애플리케이션을 실행해 본다.

2. 선긋기 클래스를 고쳐서 모든 선에 대해 동일한 두께를 사용하도록 해보자.

연습문제 1번의 선긋기(CLine) 클래스 DLL 프로젝트를 연다. 클래스 생성자 함수를 편 집해서 m_nWidth 변수를 초기화하도록 하자.

CLine::CLine()

{

m_nWidth = 원하는 굵기;

}

Trackback Address :: http://joyholic.kr/trackback/131 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/11 11:13

MSDN에 올라온 글입니다. (한글입니다. ^^)

 

단축키 리스트나, IDE Layout를 저장해서 툴바 아이콘으로 전환할 수 있는 기능들은 유용하게 사용할 수 있을것 같네요.^^

 

나머지 부분들은 C#이나 VB.NET에 관계된 내용이라.. 게임 개발자에겐 그닥 유용하진 않을듯.^^;





Visual Studio 2005 IDE 팁과 트릭

 

James Lau
Microsoft 프로그램 관리자

2007년 2월

적용 대상: Microsoft Visual Studio 2005

요약: 개발자 도구 중에 가장 인기 있는 Visual Studio 2005를 더욱 효과적으로 활용할 수 있는 몇 가지 팁과 트릭을 소개하고자 합니다. 어떤 도구든 최대한 활용하려면 익숙해지는 것이 중요한데, 개발 도구와 IDE 역시 다르지 않습니다. 그러나 C# 2.0, ASP .NET 2.0, Windows Workflow Foundation, Windows Presentation Foundation, Windows Communication Foundation과 같은 신기술이 쏟아져 나오므로 정작 Visual Studio를 익힐 시간을 내기가 어렵습니다. 10분 정도만 시간을 내어 이 기사를 읽고 Visual Studio를 보다 즐겁고 생산적으로 사용할 수 있는 유용한 정보를 얻기 바랍니다.

목차


유용한 바로 가기 키

필자가 자주 사용하는 바로 가기 키

Visual Studio에서 프로그램을 개발할 때 키보드만 사용하면 더 편할 거라고 생각한 적이 있으십니까? 고급 사용자라면 분명히 키보드 바로 가기 키를 자주 사용하여 여러 가지 작업을 보다 빠르게 수행할 것입니다. 독자들도 대부분 Debug.Start를 실행하는 F5 키, Debug.StepOver를 실행하는 F10 키, View.Properties를 실행하는 F4 키 등에는 이미 익숙할 것이라 생각합니다. 그러나 그 밖에도 잘 알려져 있지 않았지만 유용한 바로 가기 키가 몇 가지 있습니다. 아래 표에는 필자가 자주 사용하는 몇 가지 바로 가기 키가 나와 있습니다.

바로 가기 키 설명
F7 디자인 보기와 코드 보기 사이를 전환합니다.
F9 중단점을 설정하거나 해제합니다.
F12 변수, 개체 또는 함수의 정의로 이동합니다.
Ctrl+Shift+7

Ctrl+Shift+8

정의로 이동 스택에서 앞/뒤로 빠르게 이동합니다.
Shift+F12 함수나 변수의 참조를 모두 찾습니다.
Ctrl+M, Ctrl+M 편집기에서 코드 개요를 확장하거나 축소합니다.
Ctrl+K, Ctrl+C

Ctrl+K, Ctrl+U

코드 줄에 주석을 추가하거나 제거합니다.
Shift+Alt+Enter 전체 화면 모드와 표준 모드 사이를 전환합니다.
Ctrl+I 증분 검색을 실행합니다.


바로 가기 키 참조표 만들기

대부분의 사람들이 모르고 있지만 사실 Visual Studio에는 450개가 넘는 기본 바로 가기 키가 있습니다. 그러나 Visual Studio의 모든 바로 가기 키를 손쉽게 찾을 수 있는 방법은 없습니다. 모든 바로 가기 키를 열거하는 간단한 매크로를 작성하면 기본 바로 가기 키를 모두 찾아볼 수 있습니다. 다음(코드 1)은 이러한 기능을 수행하는 코드입니다.

Public Module Module1

    Public Sub ListShortcutsInHTML()

        'Declare a StreamWriter
        Dim sw As System.IO.StreamWriter
        sw = New StreamWriter("c:\\demo\\Shortcuts.html")

        'Write the beginning HTML
        WriteHTMLStart(sw)

        ' Add a row for each keyboard shortcut
        For Each c As Command In DTE.Commands
            If c.Name <> "" Then
                Dim bindings As System.Array
                bindings = CType(c.Bindings, System.Array)
                For i As Integer = 0 To bindings.Length - 1
                    sw.WriteLine("<tr>")
                    sw.WriteLine("<td>" + c.Name + "</td>")
                    sw.WriteLine("<td>" + bindings(i) + "</td>")
                    sw.WriteLine("</tr>")
                Next

            End If
        Next

        'Write the end HTML
        WriteHTMLEnd(sw)

        'Flush and close the stream
        sw.Flush()
        sw.Close()
    End Sub
Public Sub WriteHTMLStart(ByVal sw As System.IO.StreamWriter)
        sw.WriteLine("<html>")
        sw.WriteLine("<head>")
        sw.WriteLine("<title>")

        sw.WriteLine("Visual Studio Keyboard Shortcuts")
        sw.WriteLine("</title>")
        sw.WriteLine("</head>")

        sw.WriteLine("<body>")
        sw.WriteLine("<h1>Visual Studio 2005 Keyboard Shortcuts</h1>")
        sw.WriteLine("<font size=""2"" face=""Verdana"">")
        sw.WriteLine("<table border=""1"">")
        sw.WriteLine("<tr BGCOLOR=""#018FFF""><td 
align=""center""><b>Command</b></td><td 
align=""center""><b>Shortcut</b></td></tr>")


    End Sub

    Public Sub WriteHTMLEnd(ByVal sw As System.IO.StreamWriter)
        sw.WriteLine("</table>")
        sw.WriteLine("</font>")
        sw.WriteLine("</body>")
        sw.WriteLine("</html>")
    End Sub

End Module

코드 1. HTML로 바로 가기 키를 생성하는 매크로

이 매크로를 사용하려면 도구에서 매크로를 선택한 다음 매크로 IDE. . .를 선택하여 매크로 IDE를 실행합니다. MyMacros 프로젝트, MyMacros 네임스페이스를 차례로 확장한 다음 Module1을 두 번 클릭합니다. 코드 1을 매크로 IDE에 복사하고 매크로를 실행하기만 하면 됩니다. 매크로를 실행하고 나면 Visual Studio에 사용할 바로 가기 키 참조가 생성됩니다. 결과물인 C:\demo\Shortcuts.html을 열어 보십시오. 그림 1은 이 페이지의 일부입니다. 페이지를 인쇄하여 컴퓨터 옆에 붙여 두고 바로 가기 키를 익혀 보십시오.

그림 1. Visual Studio 2005 바로 가기 키 목록의 일부


바로 가기 키 사용자 지정

기본적으로 매핑되어 있지 않은 바로 가기 키도 언제든지도구 > 옵션... > 환경 > 키보드 메뉴를 통해 사용자 지정할 수 있습니다(그림 2 참조). 그러나 많은 수의 바로 가기 키를 추가하는 경우에는 자동 저장 설정 파일을 직접 편집하는 방법으로 추가하는 편이 더 쉽습니다. 이 방법을 사용하려면 다음 단계를 수행하십시오.

그림 2. 옵션 대화 상자 - 바로 가기 키 사용자 지정

1단계: 현재 바로 가기 키를 내보냅니다. 도구 > 설정 가져오기 및 내보내기. . .로 이동하여 가져오기/내보내기 설정 마법사를 시작합니다. "선택한 환경 설정 내보내기"를 선택하고 다음을 클릭합니다. "모든 설정"을 클릭하여 모든 확인란의 선택을 취소한 다음 옵션, 환경 노드를 차례로 확장하여 "키보드" 확인란을 선택합니다(그림 3). 다음을 클릭하여 마법사의 마지막 페이지로 이동합니다. 새 설정 파일의 이름을 "MyKeyboardShorcuts.vssettings"로 지정하고 경로는 기본 디렉터리로 둡니다(그림 4). 마침을 클릭합니다..

그림 3. 내보낼 키보드 설정 범주만 선택


그림 4. 설정 파일 이름을 MyKeyboardShortcuts.vssettings로 변경

2단계: 설정 파일을 열어 편집합니다. 이 파일은 My Documents\Visual Studio 2005\Settings\MyKeyboardShortcuts.vssettings에 있습니다. Visual Studio 설정 파일은 XML 파일이므로 아무 텍스트 편집기에서나 열 수 있습니다. 하지만 구문 색 지정 기능과 문서 서식 지정 기능을 사용할 수 있도록 Visual Studio 자체에서 이 파일을 여는 것이 좋습니다. 파일을 연 후에는 "Ctrl+K, Ctrl+D"를 눌러 Visual Studio가 서식을 자동으로 지정하도록 합니다. 그런 다음 <UserShortcuts> 태그를 찾습니다. 이 XML 요소에 자신만의 바로 가기 목록을 추가할 수 있습니다. 아래 코드 2에서 예를 볼 수 있습니다.

...
<UserShortcuts>
   <Shortcut Command="View.CommandWindow" Scope="Global">
Ctrl+W, Ctrl+C
</Shortcut>
   <Shortcut Command="View.SolutionExplorer" Scope="Global">
Ctrl+W, Ctrl+S
</Shortcut>
   <Shortcut Command="View.ErrorList" Scope="Global">
Ctrl+W, Ctrl+E
</Shortcut>
   <Shortcut Command="View.TaskList" Scope="Global">
Ctrl+W, Ctrl+T
</Shortcut>
   <Shortcut Command="View.Output" Scope="Global">
Ctrl+W, Ctrl+O
</Shortcut>
</UserShortcuts>
...

코드 2. 설정 파일에 바로 가기 직접 추가

이 예의 XML은 이해하기 쉽습니다. 추가하려는 각 바로 가기에 대한 <Shortcut> 요소가 있을 뿐입니다. 바로 가기 자체를 이 요소의 내용으로 지정하고 Shift, Ctrl, Alt 등의 한정자 키를 "+" 문자로 연결하여 함께 사용할 수 있습니다(예: Ctrl+Alt+J). Command 특성에는 바로 가기에 바인딩할 명령의 정식 명령 이름을 지정합니다. Scope 특성은 거의 항상 Global로 사용되므로 이에 대해서는 자세히 다루지 않겠습니다. 이 과정에서 가장 어려운 부분은 아마도 특정 명령의 정식 이름을 알아내는 부분일 것입니다. 명령의 정식 이름은 최상위 메뉴 이름과 "." 문자, 그리고 공백 없이 대/소문자가 섞인 명령 이름이 연결된 형식입니다.

바로 가기를 모두 추가한 후 파일을 저장합니다.

3단계: 설정 파일을 가져옵니다. 설정 파일에 바로 가기를 추가했으므로 이제 사용 환경으로 다시 가져올 수 있습니다. 물론 설정 파일을 다른 사람과 공유할 수도 있습니다. 설정 가져오기 및 내보내기 마법사를 다시 시작하되, 이번에는 "선택한 환경 설정 가져오기"를 선택하고 다음을 클릭합니다. "아니요, 새 설정을 가져와 현재 설정을 덮어씁니다."를 선택하고 다음을 클릭합니다. "My Settings" 폴더에서 "MyKeyboardShortcuts.vssettings"를 선택하고 다음을 클릭합니다. 기본 선택 항목을 그대로 두고 마침을 클릭합니다.


도구 설명에 바로 가기 표시

도구 모음의 명령 위로 마우스를 이동할 때 도구 설명에 바로 가기가 표시되도록 환경을 설정할 수 있습니다. 도구 > 사용자 지정. . .에서 스크린 팁에 바로 가기 키 표시 옵션이 선택되어 있는지 확인합니다.

그림 5. 도구 설명에 바로 가기 키 표시 옵션 설정


창 레이아웃 선택기

Visual Studio는 여러 가지 작업과 용도에 사용되는 다양한 도구 창을 제공하는 강력한 환경입니다. 특히 VS 2005에서 새로 제공되는 Team System 기능이 이러한 측면을 잘 보여 줍니다. 많은 사용자들이 현재 수행 중인 작업에 맞게 여러 창 레이아웃 사이를 신속하게 전환할 수 있는 기능이 있으면 좋겠다는 의견을 전해 왔는데, 사실 VS 2005에서 직접 이 기능을 구현할 수 있지만 이를 위해서는 다음과 같은 단계를 수행해야 합니다.

1단계. 설정 파일을 만듭니다. Visual Studio 2005에는 사용자가 환경 설정을 가져오거나 내보낼 수 있는 새로운 기능이 있습니다. 환경에서 사용자 지정할 수 있는 항목은 거의 모두 파일로 내보내 다른 사람과 공유하거나 다른 컴퓨터로 가져오거나 백업 파일로 저장할 수 있습니다. 가져오거나 내보낼 수 있는 설정에는 창 레이아웃, 바로 가기 키, 메뉴 사용자 지정, 글꼴 및 색, 그리고 옵션 대화 상자(도구 > 옵션. . . )의 설정 대부분이 포함됩니다. 또한 언제든지 필요에 따라 환경 설정을 모두 내보내거나 일부만 내보낼 수 있습니다.

창 선택기를 만드는 첫 단계는 사용하려는 각 창 레이아웃마다 별도의 설정 파일을 만드는 것입니다. 이 예제에서는 사용할 3개의 창 레이아웃에 해당하는 CodeWriting, CodeBrowsing 및 FormsDesign이라는 3개의 설정 파일을 만듭니다.

먼저 코드를 작성할 때 선호하는 형태로 창 레이아웃을 배치합니다. 필자의 경우 도구 창을 모두 자동 숨김 모드로 설정하여 코딩 공간을 최대한 확보한 상태로 작업할 때가 많습니다. 그림 6은 필자가 이러한 창 레이아웃에 맞게 도구 창을 어떻게 배치했는지 보여 줍니다. 각자 선호하는 방식대로 수정하여 사용하면 됩니다. 다음으로 도구 > 설정 가져오기 및 내보내기로 이동하여 설정 가져오기 및 내보내기 마법사를 시작합니다. 선택한 환경 설정 내보내기를 선택하고 다음을 클릭합니다. 창 레이아웃 확인란만 선택하고 다음을 클릭합니다. 설정 이름을 CodeWritingWinLayout.vssettings로 지정하고 마침을 클릭합니다. 필요한 세 가지 설정 파일 중 첫 번째 파일을 만들었습니다. 위 단계를 반복하여 나머지 두 가지 설정 파일을 만듭니다. 물론 창 레이아웃을 변경하고 파일 이름을 서로 다르게 지정해야 합니다. 필자의 경우 CodeBrowsingWinLayout.vssettingsFormsDesignWinLayout.vssettings로 지정했습니다.

큰 이미지를 보려면 클릭하십시오.

그림 6. 코딩 작업에 적합한 창 레이아웃(큰 이미지를 보려면 클릭하십시오.)

2단계. 설정 파일을 가져오는 매크로를 만듭니다. 설정 파일을 만든 후에는 각 설정 파일을 가져오는 매크로를 3개 만들어야 합니다. 아래 코드 3을 보면 이 코드가 얼마나 간단한지 알 수 있습니다.

Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics
Imports System.IO

Public Module Module1

  Public Sub ImportWinLayoutCodeWriting()
    DTE.ExecuteCommand("Tools.ImportandExportSettings",
    "-import:c:\demo\settings\CodeWritingWinLayout.vssettings")
  End Sub

  Public Sub ImportWinLayoutCodeBrowsing()
    DTE.ExecuteCommand("Tools.ImportandExportSettings",
    "-import:c:\demo\settings\CodeBrowsingWinLayout.vssettings")
  End Sub

  Public Sub ImportWinLayoutFormsDesign()
    DTE.ExecuteCommand("Tools.ImportandExportSettings",
    "-import:c:\demo\settings\FormsDesignWinLayout.vssettings")
End Sub

End Module

코드 3. 설정 파일을 가져오는 매크로 코드

3단계. 도구 모음에 단추를 추가합니다. 이제 창 레이아웃을 변경하는 실제 단추를 만들어야 합니다. 도구 > 사용자 지정. . .을 차례로 클릭하고 명령 탭을 클릭합니다. 범주 목록 상자에서 매크로를 선택한 다음 목록을 아래로 스크롤하여 방금 작성한 세 가지 매크로를 찾습니다. 이 세 개의 매크로는 각각 MyMacros.Module1.ImportWinLayoutCodeWriting, MyMacros.Module1.ImportWinLayoutCodeBrowsing, 및 MyMacros.Module1.ImportWinLayoutFormsDesign으로 표시됩니다(그림 7 참조). 각 명령을 클릭하여 Visual Studio 도구 모음으로 끌어 놓습니다. 이제 도구 모음에 새로 추가한 명령을 마우스 오른쪽 단추로 클릭하고 명령의 이름을 좀 더 간단하게 바꿉니다.

그림 7. 사용자 지정 대화 상자를 사용하여 도구 모음에 매크로 추가

사용자 지정 대화 상자를 닫아 사용자 지정 내용을 저장합니다. 이제 여러분만의 창 레이아웃 선택기가 완성되었습니다. 도구 모음에서 새 단추를 클릭하여 사용해 보십시오. 도구 > 옵션. . . > 환경 > 키보드 페이지로 이동하여 이 명령에 바로 가기 키를 할당할 수도 있습니다..


코드 조각

코드 조각은 Visual Studio 2005에 새로 추가된 생산성을 크게 향상시키는 기능 중 하나로, 이를 통해 for 루프 입력과 같은 지루한 입력 작업 없이 코드 조각을 빠르게 추가할 수 있습니다. 또한 이 기능은 네트워크로 데이터를 전송하는 등의 특정 작업을 수행하는 방법을 보여 주는 템플릿을 제공합니다. 기본 제공 C# 조각은 대부분 반복적인 입력 작업을 최소화하는 데 도움이 되는 첫 번째 유형이고, 기본 제공 VB 조각은 대부분 특정 작업에 대한 코드를 보다 쉽게 작성할 수 있게 해 주는 두 번째 유형입니다.

코드 조각은 두 가지 방법으로 삽입할 수 있습니다. 코드 편집기에 코드 조각의 별칭을 입력하고 Tab 키를 두 번(VB의 경우 한 번) 누르면 코드 조각을 바로 삽입할 수 있습니다. 코드 조각을 삽입한 후에는 Tab 키와 Shift+Tab을 눌러 코드 조각의 여러 필드로 이동할 수 있습니다. 이 기능을 사용하면 수정이 필요한 코드 부분을 신속하게 변경할 수 있습니다. C#의 코드 조각 별칭에는 IntelliSense도 지원됩니다. IntelliSense 목록에서는 코드 조각 아이콘을 통해 코드 조각 항목을 구별할 수 있습니다.

그림 8. 코드 조각을 완벽하게 지원하는 IntelliSense

코드 조각을 삽입할 때 코드 조각의 별칭이 기억나지 않는 경우에는 코드 편집기에서 "Ctrl+K, Ctrl+X"를 누르거나 마우스 오른쪽 단추를 누르고 코드 조각 삽입...을 선택하면 됩니다. 그러면 코드 조각 선택기가 표시되며, 여기에서 현재 프로그래밍 언어에 사용할 수 있는 모든 코드 조각을 탐색하고 삽입할 코드 조각을 선택할 수 있습니다. 이 코드 조각 삽입 방법은 C#과 Visual Basic에서 모두 사용할 수 있습니다. Visual Basic 사용자는 이 방법 외에도 코드 조각 별칭의 앞부분 몇 글자와 "?"를 입력한 다음 Tab 키를 눌러 코드 조각을 삽입할 수도 있습니다. 그러면 모든 코드 조각 별칭이 사전순으로 나열된 목록이 표시되며 입력 항목과 가장 근접한 코드 조각 별칭이 강조 표시됩니다. 이 기능은 Visual Basic 사용자에게만 제공됩니다.

큰 이미지를 보려면 클릭하십시오.

그림 9. C#에서 코드 조각 삽입(큰 이미지를 보려면 클릭하십시오.)

필자는 코드 조각 기능에서 가장 흥미로운 부분은 자신만의 코드 조각을 만들어 개인적으로 사용하거나 커뮤니티와 공유할 수 있는 점이라고 생각합니다. 물론 다른 개발자가 만든 코드 조각을 다운로드할 수도 있습니다.

Visual Studio에서 손쉽게 자신만의 코드 조각을 만들 수 있습니다. 자세한 방법은 예제를 통해 살펴보도록 하겠습니다. 필자는 작업에 도움이 될 만한 간단한 유틸리티를 자주 작성합니다. 이러한 유틸리티 중 상당수는 파일을 열고 몇 가지 처리 작업을 수행한 후 파일을 닫는 공통적인 패턴을 가집니다. 필자가 코드 조각을 만드는 방법은 다음과 같습니다.

1단계: XML 파일을 만듭니다. 각 코드 조각은 XML 파일에 들어 있습니다. Visual Studio에서 파일 > 새로 만들기. . . > 파일. . .로 이동한 다음 XML 파일 형식을 선택합니다.

그림 10. 새 XML 파일 만들기

2단계: 코드 조각을 정의합니다. 흥미롭게도 코드 조각을 만들기 위한 코드 조각도 있습니다. 파일의 둘째 줄에서 Ctrl+K, Ctrl+X를 누르고 Snippet 코드 조각을 선택하면 코드 조각 파일의 템플릿이 자동으로 삽입됩니다.

큰 이미지를 보려면 클릭하십시오.

그림 11. XML 코드 조각을 사용하여 다른 코드 조각 만들기(큰 이미지를 보려면 클릭하십시오.)

제목, 만든 이, 바로 가기 및 설명 필드는 이름만으로도 쉽게 이해할 수 있으므로 자세히 설명하지 않겠습니다. <Snippet> 태그 내의 내용에 대해서는 설명이 필요한데, 아래 예제를 살펴보는 편이 가장 이해가 빠를 것입니다.

기본적으로 </Code> 태그 내에 있는 <![CDATA[...]]> 태그에 모든 코드를 삽입하게 됩니다. 사용자가 쉽게 필드를 바꿀 수 있도록 하려면 해당 필드를 "$" 문자 한 쌍으로 감싸면 됩니다. 아래의 예제에서는 코드 조각 사용자가 StrmReader, FilePath, Line의 세 가지 리터럴을 쉽게 바꿀 수 있도록 했습니다. 이 세 가지 리터럴은 CDATA 섹션 내에서 "$" 문자 쌍과 함께 사용되었습니다. 또한 이 세 개의 리터럴은 <Declarations> 요소 내에 각각 정의해야 합니다. 각 리터럴에는 ID와 기본값(옵션)을 지정합니다.

예리한 독자는 코드 조각에 $end$라는 정의되지 않은 리터럴이 있다는 점을 알아차렸을 것입니다. 이 리터럴은 사용자가 코드 조각 필드를 모두 채운 후에 Enter 키를 눌렀을 때 커서의 위치를 지정하는 특수 리터럴입니다. 예제에는 나와 있지 않지만 $selected$라는 특수 리터럴도 있습니다. $selected$ 리터럴은 코드 조각이 SurroundsWith 유형인 경우에만 의미가 있으며 코드 감싸기...를 사용하여 이러한 유형의 코드 조각을 삽입했을 때 선택한 코드 조각이 들어갈 위치를 정의합니다.

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>File Processing</Title>
    <Author>James Lau</Author>
    <Shortcut>fp</Shortcut>
    <Description>Opens a file, does some processing, and then closes the file.</Description>
    <SnippetTypes>
      <SnippetType>SurroundsWith</SnippetType>
      <SnippetType>Expansion</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>StrmReader</ID>
        <Default>strmReader</Default>
      </Literal>
      <Literal>
        <ID>FilePath</ID>
        <Default>fPath</Default>
      </Literal>
      <Literal>
        <ID>Line</ID>
        <Default>strLine</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[         
   StreamReader $StrmReader$ = null;
   try
   {
      $StrmReader$ = new StreamReader($FilePath$);
      string $Line$;
      while (($Line$ = $StrmReader$.ReadLine()) != null)
      {
         // Perform some processing
         $selected$
         $end$
      }
   }
   catch (IOException ioex)
   {
      // Handle exception
   }
   finally
   {
      $StrmReader$.Close();
   }
    ]]>
    </Code>
  </Snippet>
</CodeSnippet>

코드 4. 샘플 코드 조각 내용


Visual Studio 시작 페이지 사용자 지정

Visual Studio 2005의 새로운 시작 페이지에는 MSDN 뉴스의 최신 정보를 제공하는 라이브 RSS 피드 외에 다른 기능도 포함되어 있습니다. 시작 페이지에서 다른 RSS 피드를 읽으려는 경우 도구를 선택하고 옵션. . ., 환경을 차례로 선택한 다음 시작 페이지를 선택하여 시작 페이지 뉴스 채널에서 URL을 편집하는 방법으로 RSS 뉴스 채널을 사용자 지정할 수 있습니다. Visual Studio를 실행할 때마다 자동으로 시작 페이지가 표시되지 않도록 하려면 같은 옵션 페이지의 시작 시에서 빈 환경 표시를 선택하여 이 동작을 변경하면 됩니다.


팀 설정

Visual Studio 2005에는 팀 설정이라는 잘 알려지지 않은 새로운 기능이 있습니다. 대부분의 개발자는 팀 환경에서 작업하는데, 이 경우 팀 설정 기능을 사용하면 보다 빠르게 팀 코딩 규칙을 적용하거나 Visual Studio를 설정할 수 있습니다.

팀 내에 코드 서식에 대한 기본 규칙 집합을 적용하려는 경우를 가정해 봅시다. 규칙을 지정하고 각 팀원이 해당 규칙에 맞게 IDE 옵션을 사용자 지정하도록 하는 대신 설정 파일을 만든 다음 팀원이 이 파일을 가리키도록 하면 간단히 해결됩니다. 팀 설정 파일이 업데이트되면 사용자가 다음 번 Visual Studio를 시작할 때 설정 파일이 자동으로 기존 설정을 덮어 씁니다. 이 기능을 활용하는 방법은 다음과 같습니다.

1단계: 설정 파일을 만듭니다. 팀 설정을 사용하여 원하는 모든 IDE 사용자 지정 내용을 적용할 수 있습니다. 개발자가 팀 설정 기능을 사용하는 가장 일반적인 설정은 코드 서식 지정 설정이겠지만 글꼴 및 색, SourceSafe 설정, 바로 가기 키, 메뉴 사용자 지정 등 내보낼 수 있는 모든 Visual Studio 설정에 이 기능을 사용할 수 있습니다. Visual Studio에서 원하는 설정을 사용자 지정한 다음 도구 > 설정 가져오기 및 내보내기. . .를 사용하여 알려진 위치로 내보내면 됩니다. 이때 다른 팀원과 공유하려는 설정 집합만 내보내는 것이 중요합니다.

2단계: UNC 경로에 설정 파일을 넣습니다. 팀원이 액세스할 수 있는 네트워크 경로에 1단계에서 내보낸 설정 파일을 복사합니다. 필자의 경우 \\jameslau\public\teamsettings.settings에서 팀 설정 파일을 공유했습니다.

3단계: 팀 설정 경로를 변경합니다. 팀원이 팀 설정 경로를 변경하여 팀 설정 파일을 가리키도록 합니다. 이 작업은 도구 > 옵션. . . > 환경 > 설정 가져오기 및 내보내기에서 수행할 수 있습니다. 팀 설정 파일 사용 확인란을 선택하고 팀 설정 파일의 경로를 지정하면 됩니다.

그림 12. 팀 설정 경로를 변경할 수 있는 옵션 대화 상자


/resetuserdata 스위치

필자가 소개할 마지막 팁은 /resetuserdata 스위치와 관련이 있습니다. 이 스위치는 Visual Studio가 복구할 수 없는 상태로 손상되었을 때 Visual Studio를 기본 상태로 재설정하는 데 사용됩니다. 이러한 문제의 예로는 창 레이아웃 파일 손상, 메뉴 사용자 지정 파일 손상 또는 바로 가기 키 파일 손상 등이 있습니다. 책임의 부인: 이 스위치를 사용하면 모든 환경 설정 및 사용자 지정이 손실됩니다. 따라서 이 스위치는 공식적으로 지원되지 않으며 Microsoft에서도 이 스위치를 공개적으로 알리지 않습니다. 즉, 명령 프롬프트에서 devenv.exe /?를 입력하더라도 이 스위치는 표시되지 않습니다. 이 스위치는 환경 문제가 발생한 경우 최후의 수단으로만 사용해야 하며, 스위치를 사용하는 경우 먼저 환경 설정을 내보내 백업해야 합니다.

이 스위치를 사용하려면 다음을 수행합니다.

  1. Visual Studio 2005의 인스턴스를 모두 종료합니다.
  2. 시작을 클릭하고 실행...을 선택합니다.
  3. "devenv.exe /resetuserdata"를 입력합니다.

이 명령을 사용하면 몇 분 동안 Visual Studio가 정리되고 처음 상태로 설정됩니다. 이때 작업 관리자를 열어 devenv.exe 프로세스가 실행 중인지 여부를 확인할 수 있습니다. 실행이 종료되면 Visual Studio를 다시 시작할 수 있습니다. 그러면 컴퓨터에서 Visual Studio를 처음으로 실행할 때처럼 처음 실행 대화 상자가 다시 표시됩니다.


결론

Microsoft는 Visual Studio에서 유용한 생산성 기능을 제공하기 위해 끊임없이 노력하고 있습니다. 여기에서 소개한 팁을 유용하게 사용하여 Visual Studio 고급 사용자가 될 수 있기를 바랍니다. Visual Studio IDE에 대한 의견이나 피드백 또는 제안 사항이 있는 경우 언제라도 jameslau@microsoft.com으로 연락하시기 바랍니다.

Trackback Address :: http://joyholic.kr/trackback/127 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/11 11:12

정의

  • google 에서 만든 C++ hash container library
  • STL과 동일한 문법, 함수로 만들어져 있으므로, STL을 사용하듯이 사용하면 된다.
  • memory를 덜 잡아먹는 'sparse' 방식, 속도가 빠른 'dense' 방식이 있다.
  • 현재 0.6 version까지 나와있다.

download

Install google hash library

  • template 라이브러리 이므로 컴파일할 필요는 없다. 헤더 파일의 경로만 지정해주면 사용할 수 있다.

사용법

Class 정의

sparse_hash_map<Key, Data, HashFcn, EqualKey, Alloc>

  • Key: Key 값의 type이다.
  • Data: 데이터 값의 type 이다.
  • HashFcn: Key 를 distribute하기 위한 hash 함수이다. 보통 hash<key type> 을 사용한지만, 직접 구현해서 쓰는 것이 성능이 더 좋을 수도 있다.
  • EqualKey: 동일한 key인지를 비교하기 위한 functor 이다. 직접 구현해주어야 한다. C++ style의 functor 를 사용하면 된다.

Key: C-style string

  • C-style string을 Key로 사용하고 싶을 때에는 다음 코드에 나온대로 사용하면 된다.

#include <iostream>
#include <google/sparse_hash_map>

using google::sparse_hash_map;      // namespace where class lives by default
using std::cout;
using std::endl;

// functor for EqualKey
struct eqstr
{
  bool operator()(const char* s1, const char* s2) const
  {
    return (s1 == s2) || (s1 && s2 && strcmp(s1, s2) == 0);
  }
};

//hash<const char*> 는 google/sparsehash/hash_fun.h 에 정의되어 있다.
int main()
{
  sparse_hash_map<const char*, int, hash<const char*>, eqstr> months;
 
  months["january"] = 31;
  months["february"] = 28;
  months["march"] = 31;
  months["april"] = 30;
  months["may"] = 31;
  months["june"] = 30;
  months["july"] = 31;
  months["august"] = 31;
  months["september"] = 30;
  months["october"] = 31;
  months["november"] = 30;
  months["december"] = 31;
 
  cout << "september -> " << months["september"] << endl;
  cout << "april     -> " << months["april"] << endl;
  cout << "june      -> " << months["june"] << endl;
  cout << "november  -> " << months["november"] << endl;
}

Key: C++ style(STL) string

  • 만약에 C++ style string을 key로 사용하고 싶다면 아래와 같이 사용한다.

struct HashString
{
 size_t operator()(const std::string _string) const
 {
  register size_t ret = 0;
  for(std::string::const_iterator it = _string.begin(); it != _string.end(); ++it )
   ret = 5 * ret + *it;

  return ret;
 }
};

struct EqualString
{
 bool operator()(const std::string& s1, const std::string& s2) const
 {
  return s1 == s2;
  //return (s1 == s2) || (s1 && s2 && strcmp(s1, s2) == 0);
 }
};

sparse_hash_map<const char*, int, HashString, EqualString> months;

성능

  • 성능 측정 결과

Average over 10000000 iterations
Wed Dec  8 14:56:38 PST 2004


SPARSE_HASH_MAP:
map_grow                  665 ns
map_predict/grow          303 ns
map_replace               177 ns
map_fetch                 117 ns
map_remove                192 ns
memory used in map_grow    84.3956 Mbytes


DENSE_HASH_MAP:
map_grow                   84 ns
map_predict/grow           22 ns
map_replace                18 ns
map_fetch                  13 ns
map_remove                 23 ns
memory used in map_grow   256.0000 Mbytes


STANDARD HASH_MAP:
map_grow                  162 ns
map_predict/grow          107 ns
map_replace                44 ns
map_fetch                  22 ns
map_remove                124 ns
memory used in map_grow   204.1643 Mbytes


STANDARD MAP:
map_grow                  297 ns
map_predict/grow          282 ns
map_replace               113 ns
map_fetch                 113 ns
map_remove                238 ns
memory used in map_grow   236.8081 Mbytes


구글 해쉬 루틴이 성능이 좋은 이유는 좋은 해쉬 펑션-데이터를 균등하게 배분해주는-를 사용하기 때문이다. 덜 최적화된 해쉬 함수는 해쉬 루틴의 성능을 저하시킨다. 예를 들어, Knuth의 Art of computer programming에 나온 알고리즘이나, SGI의 STL hash 함수는 데이터를 불균등하게 배분해주기 때문에 성능이 떨어진다.


좋은 해쉬 함수는 아래에서 구할 수 있다.

References

  1. http://google-sparsehash.googlecode.com/svn/trunk/doc/index.html
Trackback Address :: http://joyholic.kr/trackback/126 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/11 11:11

썬 마이크로시스템즈가 MMORPG 게임 개발을 돕기 위한 서버용 개발 플랫폼을 오픈 소스로 공개했다.

다크스타(Darkstar)란 명칭의 이 프로젝트는 자바 기반의 소프트웨어로 개발자들이 다양한 게임을 단일 서버 프레임워크로 접속할 수 있도록 한다. 썬은 다크스타가 멀티플레이어 온라인 게임 시장에서 중요한 기술적 기초가 될 수 있을 것이라고 밝혔다.

썬은 다크스타를 오픈 소스로 공개함으로 인해서 온라인 게임 개발자들이 가장 광범위한 시장을 개척할 수 있도록 할 것이며 엔터프라이즈급의 서버 솔루션을 구축해야 하는 짐을 덜어 줄 것이라고 주장했다.

리니지 시리즈, 시티 오브 히어로, 길드 워의 한국의 대표적인 게임 업체인 엔씨소프트(NCSoft)는 내부적으로 개발중인 게임에서 썬의 다크스타로 프로토타입 게임을 만드는데 많은 도움을 주었다고 밝혔으며 다크스타를 자사의 MMORPG 게임 개발시 적극적으로 도입할 의사를 밝혔다.

다크스타는 앞으로 수개월 이내에 GPL 라이선스 하에 오픈소스로 공식 배포될 예정이다. 다크스타 이외에 썬은 온라인 게임 개발

에 있어 썬의 서버 리소스로 접속을 가능하게 하는 다크스타 플레이그라운드(Darkstar Playground) 역시 공개했다.

기사 원문 - http://www.kbench.com/news/?cc=0&no=38882&pg=4

Trackback Address :: http://joyholic.kr/trackback/125 관련글 쓰기
Name
Password
Homepage
Secret
2007/09/09 16:44
Table
Prefix Type Description Example
b bool any boolean type bool bTrue;
c char character type char cLetter;
i int integer for index int iCars;
n int number, quantity int nNum;
l long long type long lDistance;
u unsigned unsigned type(4byte) unsigned uPercent
w WORD unsigned word(2byte) WORD wCnt
dw DWORD unsigned double word(4byte) DWORD dwLength
d double double floating point double dPercent;
f float floating point float fPercent;
s static a static variable static short ssChoice;
rg array stands for range float rgfTemp[16];
p * any pointer int *piAddr;
sz * null terminated string of characters char szText[16];
pfn * function pointer int (*pifnFunc1)(int x, int y);
t struct a user defined type ...
e enum variable which takes enumerated values ...
E enum Enumerated type ...
g_ Global Global Variable String *g_psBuffer;
m_ Member class private member variable int m_iMember;
k constant formal parameter ... void vFunc(const long klGalaxies)
r reference formal parameter ... void vFunc(long &rlGalaxies)
str String string class(C++) String strName;
prg ... dynamically allocated array char *prgGrades;
h handle handle to something hMenu
x/y ... used as size int xWitdth, yHeight;

Trackback Address :: http://joyholic.kr/trackback/81 관련글 쓰기
Name
Password
Homepage
Secret
prev"" #1 #2 next