pliki nagłówkowe #include.cpp files

0

Witam. Może ktoś podać jakiś przykład jak robić i nie wykonywać programów według tekstu ze strony o programowaniu w c++ bo nie rozumiem.

Tekst ze strony:

Although the preprocessor will happily do so, you should generally not #include .cpp files. These should be added to your project and compiled.

2

@Nikto0: To znaczy, że includować powinno się tylko pliki nagłówkowe.

0

Mogę prosić o jakieś przykłady?

3

@Nikto0: Przykłady includowania nagłówków?

#include "header1.hpp"
#include "header2.h"

Nie wiem jakiego przykładu oczekujesz

0
Eldorad O. napisał(a):

@Nikto0: Przykłady includowania nagłówków?

#include "header1.hpp"
#include "header2.h"

Nie wiem jakiego przykładu oczekujesz

A jak includować pliki nie będące nagłówkami czym to się różni?

1
Nikto0 napisał(a):

A jak includować pliki nie będące nagłówkami czym to się różni?

niczym, możesz wklejać dowolne pliki tekstowe ale trzeba pamiętać o tym że C-Preprocesor cpp oczekuje coś co w jakimś stopniu przypomina język C/C++, np. plik csv gdzie separator jest przecinkiem:

int parametry[] = {
#include "parametry.csv"
};
1

@Nikto0: Gdybyś includowal plik źródłowy zamiast naglowkowego, to miałbyś kolizje symboli poniewaz kompilator dla każdej funkcji/metody/zmienne generuje symbol bazujący na nazwie/typach argumentów/ilości argumentow - mogłem cos pomylić, ale mniej więcej tak to działa - ktory w takim przypadku się powtórzy.

2

Poprawnie (oczywiscie pomijajac rekurencje w programie):

$ cat main.cpp
#include "file1.h"
#include "file2.h"
int main() {
  func1();
  func2();
  return 0;
}
$ cat file1.cpp 
#include "file1.h"
#include "file2.h"
void func1() {
  func2();
}
$ cat file2.cpp
#include "file2.h"
#include "file1.h"
void func2() {
  func1();
}
$ cat file1.h 
#pragma once
void func1();
$ cat file2.h 
#pragma once
void func2();
$ g++ -Wall -Wextra -Werror *.cpp
$ 

Niepoprawnie:

$ cat main.cpp
#include "file1.cpp"
#include "file2.cpp"
int main() {
  func1();
  func2();
  return 0;
}
$ cat file1.cpp
#pragma once
#include "file2.cpp"
void func1() {
  func2();
}
$ cat file2.cpp
#pragma once
#include "file1.cpp"
void func2() {
  func1();
}
$ g++ -Wall -Wextra -Werror *.cpp
file1.cpp:1:9: error: #pragma once in main file [-Werror]
    1 | #pragma once
      |         ^~~~
In file included from file1.cpp:2:
file2.cpp: In function ‘void func2()’:
file2.cpp:4:3: error: ‘func1’ was not declared in this scope; did you mean ‘func2’?
    4 |   func1();
      |   ^~~~~
      |   func2
cc1plus: all warnings being treated as errors
file2.cpp:1:9: error: #pragma once in main file [-Werror]
    1 | #pragma once
      |         ^~~~
In file included from file2.cpp:2:
file1.cpp: In function ‘void func1()’:
file1.cpp:4:3: error: ‘func2’ was not declared in this scope; did you mean ‘func1’?
    4 |   func2();
      |   ^~~~~
      |   func1
cc1plus: all warnings being treated as errors
In file included from file1.cpp:2,
                 from main.cpp:1:
file2.cpp: In function ‘void func2()’:
file2.cpp:4:3: error: ‘func1’ was not declared in this scope; did you mean ‘func2’?
    4 |   func1();
      |   ^~~~~
      |   func2
$ g++ -Wall -Wextra -Werror main.cpp
In file included from file1.cpp:2,
                 from main.cpp:1:
file2.cpp: In function ‘void func2()’:
file2.cpp:4:3: error: ‘func1’ was not declared in this scope; did you mean ‘func2’?
    4 |   func1();
      |   ^~~~~
      |   func2
$ 

Sa sytuacje w ktorych wersja niepoprawna by sie skompilowala i dzialala identycznie jak poprawna, ale duzo latwiej sie o cos potknac.

2

Nie wyobrażam sobie możliwości zrozumienia skótków skUtków gdy się nie rozumie istoty.

Postaraj sie zrozumieć ideę co to jest incude - to po prostu wstawienia dobrego czy głupiego fragmentu "jakby był tutaj od zawsze"
Przy jednym jeszcze się to rozejdzie po kościach, przy złożonym projekcie będą konflikty z powodu wielokrotności "materializacji" czego, jeden wielki kocioł ze spaghetti

Dołączenie do projektu C/CPP to tzw "jednostka kompilacji", która ma pewną niezależność od innych, wnętrze ma swoje, styka się tylko po symbolach zewnętrznych (publicznych)

2

Nic lepszego nie znalazłem tak na szybko co by tłumaczyło jak działa tworzenie pliku wynikowego EXE
image

Aby powstał EXE potrzebne są tylko plik CPP
Pliki H umieszczane są wewnątrz CPP

Jezeli autor książki pisze o projekcie to pewnie trzeba znać kontekst, moze ma na mysli konkretne IDE
Możliwe że sa takie gdzie można wyklikać aby kompilator kompilował plik H ,
ja niestety uzywam cmake wiec nawet jakbym chciał to nie jestem w stanie tego zrobić

4

Technicznie rzecz biorąc, można wstawić dowolny plik przez #include, pytanie tylko, kiedy ma to sens. Na ogół jeden plik z kodem .cpp to jeden moduł. Każdy moduł kompiluje się osobno i powstają pliki objektów .o/.obj (zależnie od systemu). Są one zasadniczo prawie gotowymi programami, tylko wymagają wstawienia właściwych adresów (oznaczających zmienne i funkcje) we właściwych miejscach – tym zajmuje się linker. W plikach objektowych mamy też sygnatury funkcji i zmiennych, które nie mogą się powtarzać, więc jeśli w dwóch modułach zaimplementujemy dwie funkcje lub zmienne o tej samej nazwie (no, chyba że będą static), linker sobie z tym nie poradzi. Dlatego też mamy pliki nagłówkowe, w których są tylko informacje, że są dane funkcje/zmienne dostępne i jakie mają typy — służą one temu, żeby kompilator nie miał problemu, że używa się funkcji, której nie zna — natomiast nie ma ich implementacji, bo to powodowałoby dublowanie się objektów dla linkera.

2

Jest jeszcze jeden powód dlaczego nie powinno się includować plików .cpp. IDE (np. Visual studio) czy inne systemy budowania mogą z automatu traktować te pliki jako oddzielną jednostkę translacji i osobno kompilować a na końcu linkować. Jak się samemu skrobie Makefile to by to mogło jakoś zadziałać ale rozszerzenie cpp wskazuje na to że z tego pliku powstanie costam.o/obj.

W bibliotekach czysto nagłówkowych, w których jest zawarty cały kod trzeba zadbać o to aby definicje się nie powtórzyły. Popularnym rozwiązaniem jest tworzenie makra w stylu MYLIB_IMPLEMENTATION, które użytkownik biblioteki powinien zdefiniować tylko w jednym pliku źródłowym.

// mylib.h
#pragma once

void fun();

#ifdef MYLIB_IMPLEMENTATION
void fun() {
    // implementation
}
#endif
#define MYLIB_IMPLEMENTATION
#include "mylib.h"
int main() {
}

oczywiście taki kod biblioteki powinien mieć rozszerzenie *.h/hpp żeby IDE nie próbował tego oddzielnie kompilować. Niektórzy tworzą w projektach dla takich bibliotek krótki plik źródłowy gdzie jest definicja makra i inkdowanie, np:

// mylib_implementation.c
#define MYLIB_IMPLEMENTATION
#include "mylib.h"
0

Technicznie rzecz biorąc, można wstawić dowolny plik przez #include, pytanie tylko, kiedy ma to sens.

Unity Builds, czyli optymalizacja czasów kompilacji poprzez zmniejszanie ilości jednostek kompilacji, to w najprostszej postaci pliki .cpp do których inkluduje się pliki .cpp, które chce się razem skompilować.

1

#include jest jedną z tych katastrof *), które C (i C++ kompatybilne z C ) zabetonowały. Idea z przełomu 1950/1960 i asemblerów.

W czasie powstawania C, ok 1970 już bardzo dobrze było wiadomo, jak prawidłowo rozwijać języki z import , uses, using czy odpowiednikami, takie języki istniały i dobrze sobie żyły.

Jakimś diabelskim zrządzeniem losu powstała idea "wykastrujmy język o 10 lat wstecz aby kompilator sie łatwo pisało", nie tylko powstała, ale niestety została wypromowana.

I potem jazda ... jest problem z wielokrotnym iclude, wymyślmy guardy itd...

1 użytkowników online, w tym zalogowanych: 0, gości: 1