Search…

Conditional Compilation Directives Giải Quyết Xung Đột Include

15/09/20205 min read
Hướng dẫn sử dụng conditional directive tránh include file nhiều lần.

#include là 1 chỉ thị thường được dùng trong ngôn ngữ C/C++ để chèn các file chứa prototype hoặc các khai báo. #include hay mang đến các lỗi như chèn nhiều lần 1 nội dung.

Conditional compilation directive là gì?

Conditional compilation directive là một trong những tính năng hữu ích của bộ preprocessor (bộ tiền xử lý), bao gồm một số chỉ thị như #if, #elif, #else, #endif, #ifdef, #ifndef, ... Các conditional directives làm việc giống như các conditional operators thông thường của ngôn ngữ C/C++, do đó không khó để hiểu nó.

Xem các ví dụ sau để hiểu hơn về conditional directive:

#include <iostream>
using namespace std;

#define COUNTRY 2

#if COUNTRY==1

void Greetings()
{
	cout << "Xin Chao Viet Nam!!!" << endl;
}

#elif COUNTRY==2

void Greetings()
{
	cout << "Hello Vietnam!!!" << endl;
}

#else

void Greetings()
{
	cout << "Bonjour Vietnam!!!" << endl;
}

#endif

#define COUNTRY 2 thì sau quá trình preprocess nội dung của file header trên chỉ còn lại:

#include <iostream>
using namespace std;

void Greetings()
{
	cout << "Hello World !!!" << endl;
}

Qua ví dụ trên ta thấy conditional directive tương tự với câu lệnh điều kiện thông thường trong ngôn ngữ C/C++, biểu thức điều kiện nào đúng thì khối lệnh bên trong đó sau quá trình tiền xử lý được đưa vào mã nguồn, nếu biểu thức điều kiện sai thì khối lệnh bên dưới sẽ bị gỡ bỏ.

#include nhiều lần

Bản chất của inclusion directive là chép toàn bộ nội dung file vào, bộ tiền xử lý sẽ tìm kiếm file theo tên được ghi trong chỉ thị #include. Khi tìm thấy, bộ tiền xử lý sẽ nhận toàn bộ nội dung của file đó chèn vào file đang xét ngay tại vị trí chỉ thị #include.

Giả sử có 3 files:

  • Circle.h
  • Cylinder.h
  • main.cpp

Cylinder.h cần sử dụng các hàm của Circle.h và main.cpp cần sử dụng các hàm từ Circle.h và Cyclinder.h. Như vậy:

  • Cylinder.h cần #include "Circle.h"
  • main.cpp cần #include "Circle.h"#include "Cylinder.h"

File Circle.h

#define PI 3.141592

double area_of_a_circle(double radius)
{
	return (radius*radius*PI);
}

double circumference_of_a_circle(double radius)
{
	return (2 * radius*PI);
}

File Cylinder.h

#include "Circle.h"
double surface_area_of_a_cylinder(double radius, double height)
{
	// diện tích xung quanh = chu vi đáy * chiều cao.
	double area_of_the_side = circumference_of_a_circle(radius) * height;

	// diện tích toàn phần = 2 * diện tích đáy + diện tích xung quanh.
	return (area_of_the_side + (2 * area_of_a_circle(radius)));
}

double volume_of_a_cylinder(double radius, double height)
{
	// thể tích hình lăng trụ = diện tích đáy * chiều cao.
	return (area_of_a_circle(radius)*height);
}

Theo như cơ chế của include directive, sau quá trình tiền xử lý, trong file main.cpp của có nội dung như sau:

// NỘI DUNG SAU KHI INCLUDE Circle.h

#define PI 3.141592

double area_of_a_circle(double radius)
{
	return (radius*radius*PI);
}

double circumference_of_a_circle(double radius)
{
	return (2 * radius*PI);
}


// NỘI DUNG SAU KHI INCLUDE Cylinder.h

#define PI 3.141592

double area_of_a_circle(double radius)
{
	return (radius*radius*PI);
}

double circumference_of_a_circle(double radius)
{
	return (2 * radius*PI);
}

double surface_area_of_a_cylinder(double radius, double height)
{
	// diện tích xung quanh = chu vi đáy * chiều cao.
	double area_of_the_side = circumference_of_a_circle(radius) * height;

	// diện tích toàn phần = 2 * diện tích đáy + diện tích xung quanh.
	return (area_of_the_side + (2 * area_of_a_circle(radius)));
}

double volume_of_a_cylinder(double radius, double height)
{
	// thể tích hình lăng trụ = diện tích đáy * chiều cao.
	return (area_of_a_circle(radius)*height);
}

Trong file main.cpp mặc dù chỉ sử dụng chỉ thị include file Circle.h một lần, tuy nhiên trước đó, trong file Cylinder.h đã có chỉ thị include file Circle.h và kết quả là sau quá trình tiền xử lý, trong file main.cpp sẽ có nội dung như trên. Bước vào quá trình biên dịch, compiler phát hiện những hàm giống nhau. Do đó compiler sẽ từ chối biên dịch file main.cpp.

Để giải quyết vấn đề include nhiều lần dẫn đến xung đột trong quá trình compile, có rất nhiều cách giải quyết, nhưng trong bài viết này, tôi chỉ đề cập đến phương pháp sử dụng 2 conditional directive #ifndef#endif.

Sử dụng conditional compilation directive để tránh include nhiều lần

Ở mỗi file header, sử dụng một "cờ" để đánh dấu, nếu các cờ này chưa được định nghĩa mới cho phép thực thi chỉ thị include, nếu các cờ này đã được định nghĩa, tức là file header này đã được thực thi ở một chỉ thị include trước đó, bỏ qua chỉ thị include này để đảm bảo với mỗi một file header chỉ được include một lần.

Ví dụ trong file Circle.h ban đầu chỉnh sửa lại như sau:

#ifndef __CIRCLE_H__
#define __CIRCLE_H__

#define PI 3.141592
double area_of_a_circle(double radius)
{
	return (radius*radius*PI);
}

double circumference_of_a_circle(double radius)
{
	return (2 * radius*PI);
}

#endif

Trong chỉ thị include đầu tiên với file Circle.h, cờ __CIRCLE_H__ chưa được định nghĩa, nên sau quá trình tiền xử lý toàn bộ nội dung trong file Circle.h sẽ đuợc chèn vào vị trí của chị thị include. Đối với các chỉ thị include kế tiếp với file Circle.h, cờ __CIRCLE_H__ đã được định nghĩa, sau quá trình tiền xử lý nội dung trong file Circle.h bị bỏ qua.

Việc sử dụng các conditional directives trong file Circle.h đảm bảo việc file Circle.h chỉ được include một lần tránh gây xung đột, phát sinh lỗi trong quá trình compile.

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024