Pre-process komutu olan “include” aslında sizin çağırdığınız dosyayı okuyup, çağırdığınız dosya içerisine okunan içeriği yapıştırıyor. Bu da demek oluyor ki, siz eklediğiniz başlık dosyasında değişiklik yaptığınızda, onu eklediğiniz her dosya bundan etkileniyor. Header dosyalarının birbirlerine olan bağlılığını azaltmak ve kodun derlenme işleminin daha optimize bir hale gelmesi için “forward declaration” yöntemi tercih edilmektedir.

Ne önemi var?

Normalde bir sınıf için oluşturduğunuz header tanımlamalarını gerekli yere #include etiketi ile çağırırız, bu sayede o dosya içerisindeki tüm tanımlamalara, tüm iç değişkenlere ulaşabilir hale geliriz. Ancak bunun dez avantajı, proje büyüdükçe dosyaların birbirine olan bağımlılığının çok fazla artıyor olmasıdır bu da aynı zamanda compile time’ın artmasına sebep olacaktır. Proje içerisinde neredeyse tüm dosyalarda dahil edilmiş bir sınıfınız olduğunu varsayalım, bu sınıfta yaptığınız herhangi bir değişiklik içerisine dahil olduğu tüm sayfalara etki edebilir. Bunun önüne geçmek için header dosyası içerisinde oluşturduğumuz sınıfı include etmek yerine onu compiler’ın anlayacağı şekilde belirterek bırakabiliriz. Bu sayede header dosyamızı sınıfımızın header dosyasından bağımsız hale getirmiş oluruz.

Ayrıca derleyiciler için de önemli bir ibaredir. Normalde eklediğimiz her #include etiketi için derleyici çağırılan dosyanın içeriği kopyalar bu da hem süre hem de disk boyutu için bir külfet oluşturmaktadır.

Ne zaman ve hangi durumlarda kullanmak gerekli?

Pointer, Referans değişkenler, boyutları belli olmayan diziler en temel kullanım yerleridir. Bunlarla genel olarak incomplete type terimi ile karşılaşabilirsiniz. İçeriğinin ve boyutunun bizim için önemli olmadığı öğeleri forward declaration kullanarak tanımlayabiliriz.

Neler forward declare edilebilir?

  • Pointer ve referans türünden tanımlanmış değişkenler
  • Boyutu belli olmayan diziler
  • Bir fonksiyon parametresi veya geri dönüş tipi olan class, struct veya enum’lar
  • Underlying type’ı belirtilmiş enum’lar
  • Enum class’lar (underlying type belirtilmiş olsun veya olmasın)
  • Parametre olarak incomplete type kabul eden standart fonksiyonların parametreleri
    • unique_ptr, shared_ptr gibi smart pointer’ların parametresi olarak verilen değişkenler
    • vector, list gibi standart fonksiyonların parametresi olarak verilen pointer/referans tipi değişkenler

Aşağıda hazırlamış olduğum örnek kod üzerinden açıklamalara devam edeceğim.

// forward_declaration.h
#ifndef FORWARD_DECLARATION_H
#define FORWARD_DECLARATION_H

#include <memory>
#include <vector>
#include <list>

#include "dpdk_service.h"
#include "data_service.h"
#include "connect_service.h"
#include "pdcp_service.h"
#include "user_service.h"
#include "dpdk.h"
#include "data_plane.h"
#include "logger1.h"
#include "logger2.h"
#include "logger3.h"
#include "data_queue.h"
#include "user_service_list.h"
#include "control_plane.h"

class ForwardDeclaration {
public:
	ForwardDeclaration();
	virtual ~ForwardDeclaration();

    DPDKService* get_service(std::string name);
    void set_data_plane(DataPlane& _dataPlane);

    void set_dpdk_manager(DPDK _manager);
    DPDK get_dpdk_manager(void);

    void logger_manager(Logger1 _logger);
    void set_control_plane(std::vector<PDCPService*>& _pdcpService);
    void set_user_plane(std::list<UserService*>& _userService);
    void set_user_plane_list(std::list<UserServiceList>& _userServiceList);
private:
	void set_service(DPDKService* service);
	std::unique_ptr<DataService> _dataService;
	std::shared_ptr<ConnectService> _connectService;

    ControlPlane _controlPlane;
};

#endif //FORWARD_DECLARATION_H

//forward_declaration.cpp
#include "forward_declaration.h"

ForwardDeclaration::ForwardDeclaration(){}
ForwardDeclaration::~ForwardDeclaration(){}

DPDKService* ForwardDeclaration::get_service(std::string name)
{
    DPDKService* _dpdk = new DPDKService();

    return _dpdk;
}


Normalde bu şekilde tanımlanmış bir header dosyamız olduğunu ve varolan tanımlamaların hepsinin proje içerisinde yer alan, üst kısımda sayfaya dahil edilmiş olduğunu varsayalım. Compile sırasında bu kullanımda #include ile çağırılan tüm dosyaların içeriği sayfamıza kopyalanmış durumda. Bu compile time ve diğer dosyalara bağımlı olmak anlamına gelmektedir. Burada yapacağımız düzenlemeler sonrasında oluşmasını beklediğimiz kod yapısı ise aşağıdaki gibidir.

/*
 * forward_declaration.h
 *
 *  Created on: Oct, 27, 2020
 *      Author: Hasan Eren Keskin
 */

#ifndef FORWARD_DECLARATION_H
#define FORWARD_DECLARATION_H

#include <memory>
#include <vector>
#include <list>

#include "control_plane.h"
#include "user_service_list.h"

class DPDKService;
class DataService;
class ConnectService;
class PDCPService;
class UserService;
struct DPDK;
struct DataPlane;
//enum Logger;  // Wrong - redeclaration
enum Logger1 : uint8_t; // if defined with base type -> OK 
enum class Logger2; // if defined without base type -> OK 
enum class Logger3 : uint8_t;    // if defined with base type -> OK 

// Template class can be forward declare 
template <typename T> 
class DataQueue;

/*
    // If class is inherent class, base class must be include
    #include "base_class.h"
    class ForwardDeclaration : public BaseClass {}
*/
class ForwardDeclaration {
public:
	ForwardDeclaration();
	virtual ~ForwardDeclaration();

    // References, Pointers or Unknown sized arrays can be forward declare
    DPDKService* get_service(std::string name);
    void set_data_plane(DataPlane& _dataPlane);

    void set_dpdk_manager(DPDK _manager);
    DPDK get_dpdk_manager(void);

    void logger_manager(Logger1 _logger);
    void set_control_plane(std::vector<PDCPService*>& _pdcpService);   // vector must be include but PDCPService can be forward declare
    void set_user_plane(std::list<UserService*>& _userService);   // list must be include but UserService can be forward declare
    void set_user_plane_list(std::list<UserServiceList>& _userServiceList);   // list and UserServiceList must be include
private:
	void set_service(DPDKService* service);
	std::unique_ptr<DataService> _dataService;
	std::shared_ptr<ConnectService> _connectService;

    ControlPlane _controlPlane; // Unknown sized variable - #include must be added
};

#endif //FORWARD_DECLARATION_H
//forward_declaration.cpp
#include "forward_declaration.h"

#include "dpdk_service.h"
#include "data_service.h"
#include "connect_service.h"
#include "pdcp_service.h"
#include "user_service.h"
#include "dpdk.h"
#include "data_plane.h"
#include "logger1.h"
#include "logger2.h"
#include "logger3.h"
#include "data_queue.h"

ForwardDeclaration::ForwardDeclaration(){}
ForwardDeclaration::~ForwardDeclaration(){}

DPDKService* ForwardDeclaration::get_service(std::string name)
{
    DPDKService* _dpdk = new DPDKService();

    return _dpdk;
}


Kod içerisine baktığımız zaman, ilk halinde dahil edilmiş kütüphanelerin içerisindeki, kullanmış olduğumuz değişkenlerin/tanımlamaların neredeyse hepsini sayfamıza forward declare olarak ekledik. Ekleyemediğimiz bir tek ControlPlane kütüphanesi ve UserServiceList oldu, onun da sebebi boyutu bilinmeden verilmiş olan değişkenler olduğu için. Diğer tanımlamalarda referans veya pointer olarak tanımlanmış yapıları, bir typename parametresi olan değişkenleri, enumeration tanımlamaları, bir fonksiyonun parametresi veya return type’ı olan tanımlamaları forward olarak declare edebildik.

Header dosyamız içerisinde forward declaration yapmış olmamıza rağmen tanımlamış olduğumuz parametrelerin içerisinde yer alan member değişkenleri veya fonksiyonları kullanabilmek için çalışacağımız source dosyası içerisinde kendi kütüphanelerini çağırmamız gerekmektedir.

Kaynaklar

https://stackoverflow.com/questions/4757565/what-are-forward-declarations-in-c

http://cpp-tip-of-the-day.blogspot.com/2013/11/forward-declarations-vs-includes-in.html

https://arne-mertz.de/2018/03/forward-declaring-templates-and-enums/

https://blog.magnum.graphics/backstage/forward-declaring-stl-container-types/

https://www.iditect.com/how-to/59315843.html

https://stackoverflow.com/questions/37346/why-cant-a-forward-declaration-be-used-for-a-stdvector

https://stackoverflow.com/questions/13414652/forward-declaration-with-unique-ptr

https://stackoverflow.com/questions/71416/forward-declaring-an-enum-in-c

https://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration/553869#553869

https://stackoverflow.com/questions/21515096/c-structure-typedef-with-forward-declarations

Author

Genel dünya problemleri ile çok ilgili olmasa da teknolojik gelişmeleri yakından takip eden, sistemleri geliştirmek için çalışmalar yapan, bolca kod yazmaya çalışan ve öğrendiklerini paylaşmaya çalışan birisi.

Write A Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.