CRTP in separate .cpp & .h files

Hi,
Multiple variants of this question has been asked and answers have been given but I have a specific requirement that has been bothering me for quite a few days.

I am a hardware/fimrware engineer tinkering around with C++ so pardon my newbie questions. I have the following working:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#base.h
template<class derived>
struct base
{

	uint32_t func_1( void )
	{
		return static_cast<derived*>(this)->func_1_impl();
	}

	uint8_t func_2( void )
	{
		return static_cast<derived*>(this)->func_2_impl();
	}

};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#derived1.h
struct derived1: base< derived1 >
{

	static uint32_t func_1_impl()
	{
		std::cout << "I am derived1.func_1" << "\n";

		return true;
	}

	static uint8_t func_2_impl()
	{
		std::cout << "I am derived1.func_2" << "\n";

		return true;
	}

};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#derived2.h
struct derived2: base< derived2 >
{

	static uint32_t func_1_impl()
	{
		std::cout << "I am derived2.func_1" << "\n";

		return true;
	}

	static uint8_t func_2_impl()
	{
		std::cout << "I am derived2.func_2" << "\n";

		return true;
	}

};


1
2
3
4
5
6
7
8
9
10
11
12
13
#main.cpp

base< derived1 > 	derv_1;
base< derived2 >	derv_2;

int
main( void )
{
	derv_1.func_1();
	derv_2.func_2();

	return( 0 );
}


I don't want executable code to be in header files because of obvious reasons like debugging nightmares, multiple instantiations (header includes) etc. Can you please help me figure our how to move code in derived1.h & derived2.h to their corresponding .cpp files?

NOTE: I am not OK with implementing this using dynamic polymorphism (pure virtual functions) because of CPU execution and RAM (virtual tables) expenses.

Also not OK with "#include derived1.cpp"

Thanks in advance
uCFreak
Last edited on
I don't want executable code to be in header files because of obvious reasons like debugging nightmares, multiple instantiations (header includes) etc. Can you please help me figure our how to move code in derived1.h & derived2.h to their corresponding .cpp files?


The issues you enumerate are not issues. Where code resides doesn't make it any harder to debug. Correct header files must guard against multiple inclusion regardless of where the function definitions are, and one needn't worry about multiple function instantiations for templated functions. Your compiler/linker will readily handle those.

On the other hand, you might consider getting rid of those global objects. ;)
Hi cire,
Thanks for replying. Do you mean to say it is OK to have executable code in header files? There will be a lot *_impl functions in each file and they will call HAL APIs of a microcontroller's peripherals (GPIO, I2C, SPI etc)

Thanks
uCFreak
Also not OK with "#include derived1.cpp"

But would you be okay with #include "FOO.inl" or #include "BAZ.hidden_definitions" ?
The compiler must be able to see template definitions in the same translation unit as the declaration, so moving template definitions to a .cpp file would result in "undefined reference" errors. However, you can "hide" your code in a separate file that is merely an extension of the header file.

For your derived classes, it should be fine to move the function definitions to the appropriate .cpp files because there are no function/class templates (base has already been instantiated).
Last edited on
To be clear, no executable code is compiled when the compiler processes base.h. At the time that base.h is processed, the compiler doesn't know the type of the template argument <derived>. You can think of templates as a macro facility. The template (base) is not expanded until instantiated with an explicit type at lines 3-4 of main.cpp.

As Daleth said, derived1 and derived2 can be moved to respective .cpp files.
Please note that I or anybody else involved in this project will not be creating derivedX, we will just give an example and expect the endusers to create their own derivedX implementations.

I understand that base.h will not be expanded until the derived classes have been instantiated (main.cpp: 3, 4). I am more concerned about derivedX.h over which we have absolutely no control, we only give the template usage and it's left to the end user as to when and where they include these .cpp/.h files.


As Daleth said, derived1 and derived2 can be moved to respective .cpp files.


I do not know how to do this, can you please provide an example for this?

Thanks
I do not know how to do this...

I think you misunderstood. The classes are still in the headers, but it is the method definitions that can still go to the appropriate .cpp files.


Anyway, here are some examples.

1
2
3
4
5
6
7
8
9
10
11
12
13
//Base.h
#ifndef BASE__H__
#define BASE__H__

template <typename T>
struct Base
{
    static T transform(const T&);
};

#include "Base.inl"

#endif 

1
2
3
4
5
6
//Base.inl
template <typename T>
T Base<T>::transform(const T& var)
{
    return var ^= ~(var >> 4) ^ ~(var << 4);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
//Derived.h
#ifndef DERIVED__H__
#define DERIVED__H__

#include "Base.h"

struct Derived : Base<unsigned char>
{
     using byte_type = unsigned char;
     static byte_type d_transform(byte_type);
};

#endif 

1
2
3
4
5
6
7
//Derived.cpp
#include "Derived.h"

Derived::byte_type Derived::d_transform(byte_type b)
{
     return transform(~b);
}
Last edited on
Topic archived. No new replies allowed.