본문 바로가기

Programming/C언어 초급

C언어 초급) 02.변수 : 06. 열거형(enum)

들어가며...

열거형 변수는 단어의 의미 그대로 열거하여 사용하면 좋을 특정 대상 및 그룹을 표현할 때 사용되는 변수입니다. 이번 글에서는 열거형 타입의 특징 및 로그함수를 직접 구현해 보면서 열거형 타입의 사용법에 대해서 알아보도록 하겠습니다.

 

  • Table of Contents
    • 열거형 타입의 특징
    • 열거형 타입 사용법 및 예제

열거형 타입의 특징

열거형 타입은 범위가 명확하게 정해진 값의 모음을 표현할 때 유용한 타입입니다. 예제로 구현해 볼 로그유틸을 생각해 봅시다. 로그를 출력할 때 보통 로그 레벨(level)이 있어서 상황에 따라 TRACE, DEBUG, INFO, WARN, ERROR, FATAL등으로 구분을 지어 로그를 출력하곤 합니다.  위에 열거한 6가지 형태의 로그레벨을 정수형 타입으로 선언하여 0부터 5까지 TRACE ~ FATAL로 임의로 정하고 사용해도 무방합니다. 하지만 사용하는 입장에서도 DEBUG 레벨 로그를 출력하기 위해서는 함수의 입력 파라미터로 1이라는 수를 전달해야 할 것입니다.

이는 잘못된 개발방법은 아니지만 사용하는 사람도 다른 사람이 보았을 때도 이해하기에 쉬운 방법은 아닙니다. 이러한 경우에 해당 로그레벨의 값을 열거형으로 선언하여 사용하면 좀 더 보기좋은 코드가 될 것입니다.

 

● 열거형 타입 정의 및 선언 방법

//< 열거형 타입 정의
//< enum 태그 {멤버1, 멤버2, ...}
enum LOG_LEVEL {LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL};

//< 열거형 변수 선언
enum LOG_LEVEL level = LOG_DEBUG;

위에서 설명한 로그 레벨을 열거형 타입으로 선언하면 위와 같습니다. 이 때 LOG_LEVEL은 int, double처럼 변수의 타입처럼 사용할 수 있으며 이를 태그라고 합니다. 나머지 중괄호 안에는 해당 열거형 타입에 속하는 값들을 나열해 주면 됩니다.

 

열거형 변수는 결국 내부적으로는 정수값을 가집니다. 즉, 위에 정의한 LOG_LEVEL의 멤버 값인 LOG_TRACE부터 0으로 할당되어 순서대로 1, 2, 3을 가지게 되면 최종적으로 LOG_FATAL은 5의 값을 가지게 됩니다. 이와 같은 이유로 아래의 두 개의 비교문은 같은 의미를 갖게 됩니다.

enum LOG_LEVEL {LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL};

enum LOG_LEVEL level = LOG_DEBUG;

//< 참이므로 "debug" 출력
if(level == LOG_DEBUG) {
	printf("debug\n");
}

//< 참이므로 "debug" 출력
if(level == 1) {
	printf("debug\n");
}

이러한 멤버값의 정수값을 직접 할당 할 수도 있습니다. 아래는 정수 값을 직접 할당한 예시입니다.

//< LOG_TRACE가 1로 시작하여 LOG_FATAL은 6의 값을 갖게 됨
enum LOG_LEVEL {LOG_TRACE = 1, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL};

//< 각각의 멤버 값에 일일히 값을 할당 할 수도 있다.
enum LOG_LEVEL {LOG_TRACE = 1, LOG_DEBUG = 3, LOG_INFO = 5, LOG_WARN = 7, LOG_ERROR = 9, LOG_FATAL = 11};

//< LOG_TRACE는 1 LOG_DEBUG은 3을 가지며 LOG_INFO부터는 4, 5, ... 와 같이 순차적으로 값을 갖게 됨
enum LOG_LEVEL {LOG_TRACE = 1, LOG_DEBUG = 3, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL};

 

열거형 타입 사용법 및 예제

열거형 타입을 이용한 간단한 로그유틸 함수를 선언하고 이를 사용하는 방법에 대해서 알아보도록 하겠습니다.

 

● logger.h

/**
  PROJECT     : Sample project
  FILENAME    : logger.h
  VERSION     : 1.0
  DATE         : 2020-05-XX
*/
#ifndef _LOGGER_H_
#define _LOGGER_H_

#ifdef __cplusplus
extern "C" {
#endif
//=========================================================================//
// MODULES USED
//=========================================================================//
//=========================================================================//
// DEFINITIONS AND MACROS
//=========================================================================//
// 로그 출력 버퍼의 최대 길이
#define MAX_LOG_BUFFER_LEN              (1024)
// 로그 출력 시 포함되는 prefix의 최대 길이
#define MAX_LOG_PREFIX_LEN              (16)
//=========================================================================//
// TYPEDEFS AND STRUCTURES
//=========================================================================//
// 열거형 타입 선언
enum LOG_LEVEL {LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL};

// NULL 타입 정의
#if !defined(NULL)
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *) 0)
#endif
#endif
//=========================================================================//
//
// EXTERN FUNCTIONS
//
//=========================================================================//
// 로그 함수의 프로토타입
void logger(enum LOG_LEVEL level, const char * format, ...);


#ifdef __cplusplus
}
#endif

#endif/*_LOGGER_H_*/

 

헤더파일에는 열거형 타입 LOG_LEVEL, NULL 타입 및 logger 함수의 프로토타입을 선언하였습니다. 추가로 로그유틸에서 사용될 출력 버퍼의 최대 길이와 로그레벨의 prefix의 최대 버퍼 길이 값도 선언하였습니다.

 

● logger.c

/**
  PROJECT     : Sample project
  FILENAME    : logger.c
  VERSION     : 1.0
  DATE         : 2020-05-XX
*/
//=========================================================================//
// MODULES USED
//=========================================================================//
#include "logger.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
//=========================================================================//
// LOCAL FUNCTION PROTOTYPE
//=========================================================================//
//=========================================================================//
// EXTERN FUNTION
//=========================================================================//
/**
  desc : log utility
  return
    :
*/
void logger(enum LOG_LEVEL level, const char * format, ...) {
    int ret;
    char output[MAX_LOG_BUFFER_LEN];
    char prefix[MAX_LOG_PREFIX_LEN];
    va_list args;
    
    // initialize
    memset(output, 0x00, MAX_LOG_BUFFER_LEN);
    memset(prefix, 0x00, MAX_LOG_PREFIX_LEN);
 
    va_start(args, format);
    ret = vsprintf(output, format, args);
    va_end(args);
    
    // set the prefix
    switch (level) {
        case LOG_TRACE:
            strcpy(prefix, "[TRACE]");
            break;
        case LOG_DEBUG:
            strcpy(prefix, "[DEBUG]");
            break;
        case LOG_INFO:
            strcpy(prefix, "[INFO]");
            break;
        case LOG_WARN:
            strcpy(prefix, "[WARN]");
            break;
        case LOG_ERROR:
            strcpy(prefix, "[ERROR]");
            break;
        case LOG_FATAL:
            strcpy(prefix, "[FATAL]");
            break;
        default:
            strcpy(prefix, "[UNKNOWN]");
            break;
    }
    
    printf("%s%s", prefix, output);
}

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

//=========================================================================//
// LOCAL FUNTION
//=========================================================================//

위의 코드 전체를 이해하기 힘드실 수도 있으나 여기서는 열거형 타입에만 집중하시고 위의 코드를 간략하게 설명하면 출력으로 사용될 버퍼를 초기화하고 입력된 문자열과 인수들을 printf 함수의 포맷으로 변환합니다. 이후 입력으로 전달된 열거형 변수인 LOG_LEVEL의 값에 따라 적절한 prefix값을 선택한 후 prefix+로그 형태로 출력을 합니다.

 

● 로그유틸 사용예제

#include <stdio.h>
//< 로그유틸을 include
#include "logger.h"


/**
 Main 함수
 */
int main(int argc, const char * argv[]) {

	logger(LOG_INFO, "LOG_INFO = %d, size = %lu\n", LOG_INFO, sizeof(enum LOG_LEVEL));
    
    return 0;
}

printf() 함수가 아닌 작성한 logger함수를 이용해 로그를 출력하는 예제입니다. level값을 LOG_INFO로 하여 LOG_INFO의 실제 값과 LOG_LEVEL의 크기 값을 출력토록 하였습니다.

 

● 결과출력

 

마무리...

이상으로 간략하게 열거형 타입에 대해서 알아보았습니다. 열거형 타입은 특정 범위의 값들을 굳이 열거형으로 선언하여 사용하지 않아도 무방합니다. 하지만 열거형으로 선언하여 사용하면 위와 같이 코드에 대한 가독성이 매우 좋아집니다. 이러한 열거형 타입은 C언어뿐만 아니라 다른 언어에도 있으므로 (사용법은 조금씩 다르지만) 이러한 열거형 타입을 적극 활용하여 개발하는 습관을 들이면 한층 보기좋은 개발을 할 수 있을 것입니다.

 


U2ful은 입니다. @U2ful Corp.