Pliki nagłówkowe redefinicji języka C ++ (winsock2.h)

143

Jak zapobiec dwukrotnemu dołączaniu plików nagłówkowych? Problem w tym, że dołączam plikw MyClass.h, a następnie włączam MyClass.h do wielu plików, więc zawiera wiele razy i występuje błąd redefinicji. Jak zapobiegać?

Używam raz #pragma zamiast włączać strażników i myślę, że to w porządku.

MyClass.h:

// MyClass.h
#pragma once

#include <winsock2.h>

class MyClass
{

// methods
public:
 MyClass(unsigned short port);
 virtual ~MyClass(void);
};

EDYCJA: Kilka błędów, które otrzymuję

c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(91) : warning C4005: 'AF_IPX' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(460) : see previous definition of 'AF_IPX'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(124) : warning C4005: 'AF_MAX' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(479) : see previous definition of 'AF_MAX'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(163) : warning C4005: 'SO_DONTLINGER' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(402) : see previous definition of 'SO_DONTLINGER'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(206) : error C2011: 'sockaddr' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(485) : see declaration of 'sockaddr'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing '}' before 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing ';' before 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2059: syntax error : 'constant'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C2143: syntax error : missing ';' before '}'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(518) : warning C4005: 'IN_CLASSA' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(287) : see previous definition of 'IN_CLASSA'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(524) : warning C4005: 'IN_CLASSB' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(293) : see previous definition of 'IN_CLASSB'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(530) : warning C4005: 'IN_CLASSC' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(299) : see previous definition of 'IN_CLASSC'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(541) : warning C4005: 'INADDR_ANY' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(304) : see previous definition of 'INADDR_ANY'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(543) : warning C4005: 'INADDR_BROADCAST' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(306) : see previous definition of 'INADDR_BROADCAST'
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(577) : error C2011: 'sockaddr_in' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(312) : see declaration of 'sockaddr_in'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(132) : error C2011: 'fd_set' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(68) : see declaration of 'fd_set'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(167) : warning C4005: 'FD_SET' : macro redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(102) : see previous definition of 'FD_SET'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(176) : error C2011: 'timeval' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(111) : see declaration of 'timeval'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(232) : error C2011: 'hostent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(167) : see declaration of 'hostent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(245) : error C2011: 'netent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(180) : see declaration of 'netent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(252) : error C2011: 'servent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(187) : see declaration of 'servent'
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(264) : error C2011: 'protoent' : 'struct' type redefinition
        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(199) : see declaration of 'protoent'
akif
źródło
4
Używasz już #pragmy raz, więc powinien być uwzględniony tylko raz.
Naveen
1
Twój kompilator nie obsługuje raz pragmy?
Svetlozar Angelov
Używam programu Visual Studio 2008, dlaczego więc <winsock2.h> jest uwzględniany dwukrotnie?
akif
1
Może zostać uwzględniony dwukrotnie z niektórych dołączonych nagłówków z MyClass.h
Svetlozar Angelov
5
winsock2 i winsock mają wspólne struktury. Musisz uwzględnić tylko jeden z nich, a nie oba
Svetlozar Angelov

Odpowiedzi:

234

Ten problem występuje w przypadku dołączania <windows.h>wcześniej <winsock2.h>. Spróbuj ułożyć listę włączeń, która <windows.h>jest dołączona po <winsock2.h>lub zdefiniowana jako _WINSOCKAPI_pierwsza:

#define _WINSOCKAPI_    // stops windows.h including winsock.h
#include <windows.h>
// ...
#include "MyClass.h"    // Which includes <winsock2.h>

Zobacz także to .

pingw33n
źródło
W ogóle nie uwzględniam <windows.h>, wiem, że <winsock2.h> robi to za mnie.
akif
2
Dla mnie twój kod kompiluje się ok tylko <winsock2.h>w MSVC2008. <windows.h>Włączenie powoduje, że generuje identyczne błędy kompilacji, jak podałeś.
pingw33n
Czy <windows.h> jest uwzględniony w stdafx.h?
Colin Desmond
1
To rozwiązanie rozwiązało problem w VS 2010 z SDK 7.1. Dzięki pingw33n!
adamfisk
Miałem #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h>w porządku i otrzymywałem winsock2, plik h nie został znaleziony. Zawarte #define _WINSOCKAPI_ przede wszystkim 3 zawiera wciąż ten sam błąd
Ava
75

Jak sugerowali inni, problem polega na tym, kiedy windows.hzostało uwzględnione wcześniej WinSock2.h. Ponieważ windows.hobejmuje winsock.h. Nie możesz używać obu WinSock2.hi winsock.h.

Rozwiązania:

  • Uwzględnij WinSock2.hprzed windows.h. W przypadku prekompilowanych nagłówków należy to tam rozwiązać. W przypadku prostego projektu jest to łatwe. Jednak w dużych projektach (szczególnie przy pisaniu kodu przenośnego, bez prekompilowanych nagłówków) może to być bardzo trudne, ponieważ gdy twój nagłówek WinSock2.hjest dołączony, windows.hmoże być już dołączony z innego pliku nagłówka / implementacji.

  • Zdefiniuj WIN32_LEAN_AND_MEANprzed windows.hlub w całym projekcie. Ale wykluczy wiele innych rzeczy, których możesz potrzebować i powinieneś dołączyć je samodzielnie.

  • Zdefiniuj _WINSOCKAPI_przed windows.hlub w całym projekcie. Ale po uwzględnieniu WinSock2.hotrzymasz ostrzeżenie o przedefiniowaniu makra.

  • Użyj windows.hzamiast WinSock2.hkiedy winsock.hjest wystarczające dla twojego projektu (w większości przypadków jest). Prawdopodobnie spowoduje to wydłużenie czasu kompilacji, ale rozwiązuje wszelkie błędy / ostrzeżenia.

Pavel Machyniak
źródło
14
WIN32_LEAN_AND_MEANbyło dla mnie rozwiązaniem często
Jonatan Cloutier
O _WINSOCK_rozwiązaniu: Nie powinieneś grtować ostrzeżenia przed redefinicją makr, jeśli obie definicje są identyczne. Częstym błędem jest to, że ludzie dodają definicję do projektu bez ustawiania żadnej wartości i oczekują pustej definicji. Jednakże, jeśli dodasz -D_WINSOCK_do linii cmd, ustawi się ona _WINSOCK_na 1. Aby utworzyć pustą definicję, -D_WINSOCK_=należy ją przekazać.
Paweł Stankowski
Jeśli używasz #define _WINSOCKAPI_, możesz również potrzebować #define _WINSOCK_DEPRECATED_NO_WARNINGS, w zależności od okoliczności.
Lorien Brune
16

Aha - brzydota Windowsa ... Kolejność włączeń jest tutaj ważna. Musisz dołączyć winsock2.h przed windows.h. Ponieważ windows.h jest prawdopodobnie zawarty w twoim prekompilowanym nagłówku (stdafx.h), będziesz musiał dołączyć winsock2.h stamtąd:

#include <winsock2.h>
#include <windows.h>
Daniel Paull
źródło
14

Używając „osłon nagłówka”:

#ifndef MYCLASS_H
#define MYCLASS_H

// This is unnecessary, see comments.
//#pragma once

// MyClass.h

#include <winsock2.h>

class MyClass
{

// methods
public:
    MyClass(unsigned short port);
    virtual ~MyClass(void);
};

#endif
DevSolar
źródło
2
Chyba się mylę (do tej pory 4 głosy za), ale myślę, że używanie włączonych strażników to to samo, co kiedyś pragma, umieściłeś ich obu?
Svetlozar Angelov
1
Cóż, mam raz #pragma, który afaik to te same osłony nagłówka
akif
2
@Angelov: Tak, mówię, że to te same rzeczy. Problem nie dotyczy moich plików nagłówkowych, ale myślę, że sam plik <winsock2.h> nie ma zabezpieczeń nagłówka lub może być czymś innym.
akif
1
Z definicji #pragma jest zależna od kompilatora (niestandardowa). Może nie działać na wszystkich kompilatorach. Wiem, że Visual Studio raz akceptuje #pargma. Nie jestem pewien, czy tak jest. Wiem, że to strażnicy ZAWSZE działają. Używam obu #pragma raz i dołączam osłony dla maksymalnej przenośności. Wygląda na to, że MSVC raz zoptymalizowało obsługę #pragma, a gcc zoptymalizowało obsługę włączników. Jedyną różnicą w stosunku do mojego standardowego nagłówka jest to, że #praga raz znajduje się poza osłonami włączania.
KitsuneYMG
1
Polecenie „#pragma” jest określone w standardzie ANSI w celu uzyskania dowolnego efektu zdefiniowanego w implementacji. W preprocesorze GNU C „#pragma” pierwsze próby uruchomienia gry „łobuz”; jeśli to się nie powiedzie, próbuje uruchomić grę „hack”; jeśli to się nie powiedzie, próbuje uruchomić GNU Emacsa wyświetlającego Wieżę Hanoi; jeśli to się nie powiedzie, zgłasza błąd krytyczny. W każdym razie przetwarzanie wstępne nie jest kontynuowane. - Richard M. Stallman, The GNU C Preprocessor, wersja 1.34
DevSolar
6

Napotkałem ten problem, próbując wyciągnąć pakiet innej firmy, który najwyraźniej zawierał windows.h gdzieś w bałaganie nagłówków. Definiowanie _WINSOCKAPI_na poziomie projektu było znacznie łatwiejsze (nie wspominając o łatwiejszym w utrzymaniu) niż brodzenie przez zupę i naprawianie problematycznego dołączenia.

Yaur
źródło
1
Na Qt w pliku .pro wygląda to tak: DEFINES += _WINSOCKAPI_
phyatt
@phyatt: powinieneś zamienić to w odpowiedź, jeśli tego nie zrobisz, ja to zrobię!
Leif Gruenwoldt
@LeifGruenwoldt idź po to! Cieszę się, że mogłem pomóc.
phyatt
6

W VS 2015 będą działać:

#define _WINSOCKAPI_

Podczas gdy następujące nie będą:

#define WIN32_LEAN_AND_MEAN
MariuszW
źródło
6

Sprawdziłem rekurencyjne obejmuje dostrzegłem pliki nagłówkowe, które obejmują (rekurencyjnie) niektóre #include "windows.h"a #include "Winsock.h"i napisać #include "Winsock2.h". w tych plikach dodałem #include "Winsock2.h"jako pierwsze dołączenie.

Tylko kwestia cierpliwości, spojrzenie obejmuje jeden po drugim oraz stworzenie takiej kolejności, najpierw #include "Winsock2.h"następnie#include "windows.h"

kiriloff
źródło
5

Znalazłem ten link windows.h i winsock2.h, który ma alternatywę, która działała świetnie:

#define _WINSOCKAPI_    // stops windows.h including winsock.h
#include <windows.h>
#include <winsock2.h>

Miałem problem ze znalezieniem miejsca, w którym wystąpił problem, ale po dodaniu tego #define udało mi się zbudować bez rozwiązania problemu.

Benjamin Herreid
źródło
4

Nie użyłbym tylko FILENAME_H, ale

#ifndef FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD
#define FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD

//code stuff
#endif // FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD

Zawsze używałem guidu Postfix. Kilka lat temu natknąłem się na bardzo słabą bazę kodu, która miała różne pliki nagłówkowe o tej samej nazwie i zawierała ochronę. Wspomniane pliki zdefiniowały klasę o tej samej nazwie. Gdyby użyto tylko przestrzeni nazw. Niektóre projekty zostały skompilowane, inne nie. Użycie unikalnych osłon było częścią rozwiązania polegającego na rozróżnieniu nagłówków i ich zawartości.

W systemie Windows z programem Visual Studio użyj guidgen.exe, w systemie Linux uuidgen -t.

Sam
źródło
4

Napotkałem ten sam problem i oto, co odkryłem do tej pory:

Z tego fragmentu wyjściowego -

c: \ program files \ microsoft sdks \ windows \ v6.0a \ include \ ws2def.h (91): ostrzeżenie C4005: 'AF_IPX': redefinicja makra
c: \ program files \ microsoft sdks \ windows \ v6.0a \ include \ winsock.h (460): zobacz poprzednią definicję „AF_IPX”

-Wydaje się, że zarówno ws2def.h, jak i winsock.h zostały uwzględnione w Twoim rozwiązaniu.

Jeśli spojrzysz na plik ws2def.h, zaczyna się on od następującego komentarza -

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:

    ws2def.h

Abstract:

    This file contains the core definitions for the Winsock2
    specification that can be used by both user-mode and 
    kernel mode modules.

    This file is included in WINSOCK2.H. User mode applications
    should include WINSOCK2.H rather than including this file
    directly. This file can not be included by a module that also
    includes WINSOCK.H.

Environment:

    user mode or kernel mode

--*/

Zwróć uwagę na ostatnią linię - „Ten plik nie może być dołączony przez moduł, który zawiera również WINSOCK.H”

Nadal próbuję rozwiązać problem bez wprowadzania zmian w kodzie.

Daj mi znać, jeśli to ma sens.

Shailesh Tainwala
źródło
2

Powinieneś użyć ochrony nagłówka.

umieść te linie na górze pliku nagłówkowego

#ifndef PATH_FILENAME_H
#define PATH_FILENAME_H

i na dole

#endif
ntcong
źródło
1
#pragma raz i włącz strażników to te same rzeczy, prawda?
akif
Nie są do końca takie same - osłony nagłówka zapobiegną ponownemu włączeniu pliku na poziomie preprocesora, a ponadto są oczywiście odrobinę bardziej przenośne niż kiedyś #pragma.
Timo Geusch
1
Chodziło mi o to, że są zbudowane do tych samych celów :)
akif
2
#pragma raz jest niestandardowa,
afaik
2

#pragma onceopiera się na pełnej ścieżce nazwy pliku. Więc prawdopodobnie masz dwie identyczne kopie MyClass.h lub Winsock2.h w różnych katalogach.

soru
źródło
dowiązanie symboliczne lub węzeł NTFS również spowoduje uszkodzenie systemu.
Thomi,
1

#pragma oncejest niestabilny, nawet na kompilatorach MS i nie jest obsługiwany przez wiele innych kompilatorów. Jak wiele innych osób wspomniało, najlepszym rozwiązaniem jest użycie osłon włączających. W ogóle nie używaj #pragma once- znacznie ułatwi ci to życie.

Thomi
źródło
3
Niestety, widziałem więcej niż zero nieudanych włączonych strażników, gdzie literówka oznacza, że ​​strażnik w rzeczywistości nie działa, lub gdy pliki o tej samej nazwie w różnych katalogach używają tego samego tokena, albo gdy używany token zaczyna się od podwójnego podkreślenie lub podkreślenie, a następnie wielka litera (i dlatego jest nieprzenośne, tak jak kiedyś #pragma). Tak więc w przypadku kodu z natury nieprzenośnego, jak w przypadku czegokolwiek używającego winsock.h, #pragma nie martwił mnie, aż do momentu, w którym powiedziałeś, że jest niestabilny. Kiedy to się nie udaje, poza brakiem wsparcia?
Steve Jessop,
3
Podczas używania #pragma oncekompilator przyjmuje nazwę węzła pliku nagłówka jako unikalny identyfikator. Może się to nie powieść, jeśli masz dowiązania symboliczne lub połączenia NTFS w drzewie źródłowym (częściej niż myślisz), lub nawet jeśli masz plik o tej samej nazwie w innym katalogu systemowym (zdarzyło mi się to wcześniej, gdy wersja 1 i wersja 2 tej samej biblioteki zainstalowanej w dwóch różnych systemach zawierają ścieżki). Podsumowując: wolę mieć większą kontrolę i żyć z okazjonalnym błędem oprogramowania wetware, niż ufać kompilatorowi, że zrobi to za mnie.
Thomi
1

W moim projekcie (używam VS 2008 SP1) działa następne rozwiązanie:

Plik nagłówkowy:

//myclass.h
#pragma once
#define _WINSOCKAPI_
#include <windows.h>

Klasa CPP:

//myclass.cpp
#include "Util.h"
#include "winsock2class.h"
#pragma comment(lib, "Ws2_32.lib")

gdzie #include „winsock2class.h” oznacza klasę, która zaimplementowała winsock2.h:

//winsock2class.h
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "Ws2_32.lib")
Yahor M
źródło
0

Właściwie natknąłem się na problem, w którym musiałem zdefiniować winsock2.h jako pierwsze dołączenie, wydaje się, że ma inne problemy z dołączeniami z innych pakietów. Mam nadzieję, że jest to pomocne dla kogoś, kto napotka ten sam problem, nie tylko windows.h, ale wszystkie zawiera.

Jeff
źródło