Заголовочные файлы переопределения C ++ (winsock2.h)

Как предотвратить двойное включение файлов заголовков? Проблема в том, что я включаю в MyClass.h, а затем включаю MyClass.h во многие файлы, поэтому он включает несколько раз и возникает ошибка переопределения. Как предотвратить?

Я использую #pragma once вместо включения охранников, и я думаю, что это нормально.

MyClass.h:

// MyClass.h
#pragma once

#include <winsock2.h>

class MyClass
{

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

РЕДАКТИРОВАТЬ: Я получаю несколько ошибок

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'

person akif    schedule 03.09.2009    source источник
comment
Вы уже используете #pragma один раз, поэтому его следует включать только один раз.   -  person Naveen    schedule 03.09.2009
comment
Ваш компилятор ни разу не поддерживает прагму?   -  person Svetlozar Angelov    schedule 03.09.2009
comment
Я использую Visual Studio 2008, почему тогда ‹winsock2.h› включается дважды?   -  person akif    schedule 03.09.2009
comment
Он может быть включен дважды из некоторых заголовков из MyClass.h   -  person Svetlozar Angelov    schedule 03.09.2009
comment
тогда как исправить проблему?   -  person akif    schedule 03.09.2009
comment
@Manzoor: это ошибка, вы получаете фатальную ошибку C1189: #error: WINDOWS.H уже включен. Приложения MFC не должны включать #include ‹windows.h›   -  person Naveen    schedule 03.09.2009
comment
winsock2 и winsock имеют общие структуры. Вы должны включить только один из них, а не оба   -  person Svetlozar Angelov    schedule 03.09.2009


Ответы (16)


Эта проблема возникает при включении <windows.h> до <winsock2.h>. Попробуйте организовать свой список включения так, чтобы <windows.h> был включен после <winsock2.h>, или сначала определите _WINSOCKAPI_:

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

См. Также это .

person pingw33n    schedule 03.09.2009
comment
Я вообще не включаю ‹windows.h›, я знаю, что ‹winsock2.h› делает это за меня. - person akif; 03.09.2009
comment
Для меня ваш код нормально компилируется только с <winsock2.h> в MSVC2008. <windows.h> включение заставляет его генерировать идентичные ошибки компиляции, как вы указали. - person pingw33n; 03.09.2009
comment
Включен ли ‹windows.h› в stdafx.h? - person Colin Desmond; 04.10.2009
comment
Это решение устранило у меня проблему на VS 2010 с SDK 7.1. Спасибо, pingw33n! - person adamfisk; 24.09.2011
comment
У меня был #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> в порядке, и я получал winsock2, файл h не найден. Включено #define _WINSOCKAPI_ выше всех 3 содержит ту же ошибку - person Ava; 08.03.2012
comment
Раньше сталкивался с этой проблемой несколько раз и обычно решал ее, сначала включив winsock2.h. Не знал о WINSOCKAPI, и это упростило работу в этом проекте. Спасибо, pingw33n. - person Lucky Luke; 15.08.2012
comment
У меня возникли проблемы с удалением всех включений winsock.h, хотя я обнаружил все свои включения windows.h. В конце концов я решил это, добавив _WINSOCKAPI_ к параметрам проекта в разделе «Свойства» - ›C / C ++ -› Препроцессор, таким образом убедившись, что он был определен перед компиляцией. - person icecream; 20.06.2013
comment
Спасибо!!! Я не включаю ни один из них, но некоторые другие файлы, которые я включаю, включены. Ваше сообщение заставило меня изменить порядок, в котором я включаю эти файлы, и это сработало. Вы только что сэкономили мне часы на поиски, чтение и ругань. - person Nitzan Tomer; 08.08.2013
comment
Я использую VS2013. Помещение _WINSOCKAPI_ в Project Properties > Configuration Properties > C/C++ > Preprocessor > Preprocessor definitions для конфигураций Debug и Release помогло мне. - person ancajic; 05.10.2016
comment
@ pingw33n Спасибо! помог скомпилировать код C ++ с VS2014 - person sqr163; 07.07.2017

Как предполагали другие, проблема заключается в том, что windows.h включен до WinSock2.h. Поскольку windows.h включает winsock.h. Вы не можете использовать одновременно WinSock2.h и winsock.h.

Решения:

  • Включите WinSock2.h перед windows.h. В случае предварительно скомпилированных заголовков вы должны решить это там. В случае простого проекта это легко. Однако в больших проектах (особенно при написании переносимого кода без предварительно скомпилированных заголовков) это может быть очень сложно, потому что, когда ваш заголовок с WinSock2.h включен, windows.h уже может быть включен из другого файла заголовка / реализации.

  • Определите WIN32_LEAN_AND_MEAN перед windows.h или в масштабе проекта. Но это исключит многие другие вещи, которые могут вам понадобиться, и вы должны включить их самостоятельно.

  • Определите _WINSOCKAPI_ перед windows.h или в масштабе проекта. Но когда вы включаете WinSock2.h, вы получаете предупреждение о переопределении макроса.

  • Используйте windows.h вместо WinSock2.h, если для вашего проекта достаточно winsock.h (в большинстве случаев это так). Это, вероятно, приведет к увеличению времени компиляции, но решит любые ошибки / предупреждения.

person Pavel Machyniak    schedule 28.11.2011
comment
WIN32_LEAN_AND_MEAN был решением для меня много танков - person Jonatan Cloutier; 30.06.2012
comment
О _WINSOCK_ решении: не следует выдавать предупреждение о переопределении макроса, если оба определения идентичны. Распространенная ошибка заключается в том, что люди добавляют определение в проект, не устанавливая никакого значения, и ожидают пустого определения. Однако, если вы добавите -D_WINSOCK_ в строку cmd, она установит _WINSOCK_ в 1. Чтобы создать пустое определение, необходимо передать -D_WINSOCK_=. - person Paweł Stankowski; 09.01.2017
comment
Если вы используете #define _WINSOCKAPI_, вам также может понадобиться #define _WINSOCK_DEPRECATED_NO_WARNINGS, в зависимости от ваших обстоятельств. - person Lorien Brune; 30.09.2017

Эх - уродство винды ... Здесь важен порядок включения. Вам нужно включить winsock2.h перед windows.h. Поскольку windows.h, вероятно, включен из вашего предварительно скомпилированного заголовка (stdafx.h), вам нужно будет включить winsock2.h оттуда:

#include <winsock2.h>
#include <windows.h>
person Daniel Paull    schedule 15.07.2010

Используя «защиту заголовка»:

#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
person DevSolar    schedule 03.09.2009
comment
Думаю, я ошибаюсь (к настоящему времени 4 голоса), но я думаю, что использование include guards - это то же самое, что и прагма один раз, вы поместили их оба? - person Svetlozar Angelov; 03.09.2009
comment
Ну, у меня есть #pragma once, который afaik такой же заголовок гвардии - person akif; 03.09.2009
comment
@Angelov: Да, это то, что я говорю, это одно и то же. Проблема не в моих файлах заголовков, но я думаю, что сам ‹winsock2.h› не имеет защиты заголовков или может быть чем-то еще. - person akif; 03.09.2009
comment
@Manzoor: В чем проблема, которую вы пытаетесь решить? - person Naveen; 03.09.2009
comment
Проблема в том, что я включаю ‹winsock2.h› в свой заголовок, а затем в другой заголовок, я включаю свой предыдущий файл заголовка, и поэтому происходит переопределение структур WinSock2. Почему? - person akif; 03.09.2009
comment
#ifndef/#defined include guards - это полностью переносимый стандарт c ++. Поведение #pragma once зависит от реализации, хотя многие реализации заставляют его вести себя точно так же, как и защитники включения. - person CB Bailey; 03.09.2009
comment
#pragma когда-то полагается на компилятор, это не то же самое, что защита заголовков, которая более переносима - person ntcong; 03.09.2009
comment
По определению #pragma зависит от компилятора (нестандартно). Это может работать не на всех компиляторах. Я знаю, что Visual Studio принимает #pargma один раз. Я не уверен, что это делает gcc. Знаю, что включать охрану ВСЕГДА работают. Я использую оба #pragma один раз и включаю охранников для максимальной безопасности. Кажется, что MSVC оптимизировал обработку #pragma once, а gcc оптимизировал обработку включаемых охранников. Единственное отличие от моего стандартного заголовка состоит в том, что #praga once находится вне защиты включения. - person KitsuneYMG; 03.09.2009
comment
Команда '#pragma' определена в стандарте ANSI для произвольного эффекта, определяемого реализацией. В препроцессоре GNU C команда '#pragma' сначала пытается запустить игру 'rogue'; если это не удается, он пытается запустить игру «взломать»; если это не удается, он пытается запустить GNU Emacs, отображающий Ханойскую башню; если это не удается, он сообщает о фатальной ошибке. В любом случае предварительная обработка не продолжается. - Ричард М. Столмен, Препроцессор GNU C, версия 1.34 - person DevSolar; 03.09.2009
comment
В вышеприведенном комментарии подчеркивается, что #pragma что-то делает, в то время как #ifndef / #define / #endif является полностью переносимым, совместимым со стандартами и рекомендуемым методом. @ Манзур Ахмед: Попробуйте свести проблему к минимуму кода, необходимому для ее воспроизведения. Это очень полезная практика для устранения таких проблем. - person DevSolar; 03.09.2009
comment
@ Светлозар Ангелов: Я сохранил #pragma один раз из исходного кода, не будучи на 100% уверен, что он делает, и просто обернул все это целиком. В любом случае не должно быть больно. - person DevSolar; 03.09.2009
comment
Тогда ваша проблема не связана с множественным включением MyClass.h. Еще раз: сделайте копию своего кода, затем методично вырезайте файлы и строки исходного кода, которые не нужны для воспроизведения ошибки, пока не увидите проблему. Поверьте, работает каждый раз. - person DevSolar; 03.09.2009
comment
pragma one, как известно, быстрее, чем защита заголовков, но это нестандартно. Поэтому иногда разработчик использует их оба. - person ntcong; 03.09.2009
comment
@DevSolar: ты уже устарел. GCC один раз поддерживал #pragma в течение многих лет. - person Steve Jessop; 03.09.2009
comment
@onebyone: Цитата Ричарда Столмена была юмором. Знаешь? Например, когда вы говорите людям, что, полагаясь на неопределенное поведение, можно отформатировать их жесткие диски ... - person DevSolar; 03.09.2009
comment
Я знаю, но спрашивающий не полагается на неопределенное поведение, и цитата Столлмана неприменима. Он полагается на определенное поведение своего компилятора (просто не определенное в стандарте C ++). Итак, #pragma ничего не делает, а делает именно то, что написано в документации MS. Обвините меня в бессмысленности, я обвиню вас в том, что вы сорвали разговор, кто-то может вмешаться в нацистскую аналогию, и у нас будет усмешка trifecta ;-) - person Steve Jessop; 03.09.2009
comment
Хрр, хрр ... Я в игре. :-D Однако вы можете заметить, что в исходном неотредактированном посте, о котором я говорил, не указывался используемый компилятор ... - person DevSolar; 03.09.2009
comment
Да, верно. Сейчас я планирую фильтр для SO, который просматривает фрагменты кода в вопросах, идентифицирует специфичный для компилятора код и заставляет спрашивающего указывать компилятор, если он есть. Однако обнаружение кода, основанного на арифметике дополнения до 2, может быть непростой задачей ... - person Steve Jessop; 03.09.2009

Я столкнулся с этой проблемой, когда пытался вытащить сторонний пакет, который, по-видимому, включал windows.h где-то в его беспорядке заголовков. Определить _WINSOCKAPI_ на уровне проекта было намного проще (не говоря уже о более удобном для сопровождения), чем пробираться через их суп и исправлять проблемное включение.

person Yaur    schedule 04.10.2009
comment
В Qt в файле .pro это выглядит так: DEFINES += _WINSOCKAPI_ - person phyatt; 04.04.2014
comment
@phyatt: ты должен превратить это в ответ, если ты не сделаешь этого, я сделаю это! - person Leif Gruenwoldt; 05.02.2015
comment
@LeifGruenwoldt дерзай! Рад, что смог помочь. - person phyatt; 05.02.2015

Я проверил рекурсивные включения, я обнаружил файлы заголовков, которые включают (рекурсивно) некоторые #include "windows.h" и #include "Winsock.h" и написали #include "Winsock2.h". в эти файлы я добавил #include "Winsock2.h" в качестве первого включения.

Просто наберитесь терпения, просмотрите все по очереди и установите этот порядок, сначала #include "Winsock2.h", затем #include "windows.h".

person kiriloff    schedule 25.08.2014
comment
В моем случае включение windows.h в качестве ПОСЛЕДНЕГО из всех включает удаленную ошибку компилятора. - person Elmue; 16.11.2020

В VS 2015 будет работать следующее:

#define _WINSOCKAPI_

В то время как следующее не будет:

#define WIN32_LEAN_AND_MEAN
person MariuszW    schedule 22.05.2017

Я нашел эту ссылку windows.h и winsock2.h, у которого есть альтернатива, которая отлично мне подошла:

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

У меня возникли проблемы с поиском места возникновения проблемы, но, добавив этот #define, я смог построить, не выясняя его.

person Benjamin Herreid    schedule 01.04.2011

Я бы не стал использовать только FILENAME_H, но

#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

Я всегда использовал postfix guid. Несколько лет назад я столкнулся с очень плохой базой кода, в которой были разные файлы заголовков с одним и тем же именем и включали защиту. В рассматриваемых файлах был определен класс с таким же именем. Если бы использовались только пространства имен. Некоторые проекты были скомпилированы, некоторые нет. Использование уникальных защитных элементов было частью решения, позволяющего различать заголовки и их содержимое.

В Windows с Visual Studio используйте guidgen.exe, в Linux - uuidgen -t.

person Sam    schedule 03.09.2009

Я столкнулся с той же проблемой, и вот что я обнаружил до сих пор:

Из этого выходного фрагмента -

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'

-Похоже, что в ваше решение включены и ws2def.h, и winsock.h.

Если вы посмотрите на файл ws2def.h, он начинается со следующего комментария -

/*++

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

--*/

Обратите внимание на последнюю строчку - «Этот файл не может быть включен модулем, который также включает WINSOCK.H»

Все еще пытаюсь исправить проблему, не внося изменений в код.

Дайте мне знать, если в этом есть смысл.

person Shailesh Tainwala    schedule 09.01.2012

#pragma once основан на полном пути имени файла. Так что, скорее всего, у вас есть две идентичные копии MyClass.h или Winsock2.h в разных каталогах.

person soru    schedule 03.09.2009
comment
символическая ссылка или соединение NTFS также вызовут поломку системы. - person Thomi; 03.09.2009

Вы должны использовать защиту жатки.

поместите эту строку в верхнюю часть файла заголовка

#ifndef PATH_FILENAME_H
#define PATH_FILENAME_H

и внизу

#endif
person ntcong    schedule 03.09.2009
comment
#pragma once и включить охранников - это тоже самое, не так ли? - person akif; 03.09.2009
comment
Они не совсем то же самое - защита заголовков предотвратит повторное включение файла на уровне препроцессора, к тому же они, очевидно, более переносимы, чем #pragma once. - person Timo Geusch; 03.09.2009
comment
Я имел в виду, что они созданы для тех же целей :) - person akif; 03.09.2009
comment
#pragma Once нестандартный, афаик - person ntcong; 03.09.2009

#pragma once нестабилен даже в компиляторах MS и не поддерживается многими другими компиляторами. Как уже упоминали многие другие люди, использование include guards - лучший вариант. Ни в коем случае не используйте #pragma once - это сделает вашу жизнь намного проще.

person Thomi    schedule 03.09.2009
comment
К сожалению, я видел больше нуля ошибочных включенных охранников, где опечатка означает, что защита на самом деле не работает, или когда файлы с одним и тем же именем в разных каталогах используют один и тот же токен, или где используемый токен начинается с двойного подчеркивание или подчеркивание, затем заглавная буква (и, следовательно, не переносится, как #pragma once). Так что для непереносимого по своей сути кода, такого как любой другой код, использующий winsock.h, #pragma однажды меня глубоко не беспокоил, вплоть до того момента, когда вы сказали, что это ненадежно. Когда происходит сбой, кроме того, что он вообще не поддерживается? - person Steve Jessop; 03.09.2009
comment
При использовании #pragma once компилятор принимает имя узла файла заголовка в качестве уникального идентификатора. Это может потерпеть неудачу, если у вас есть символические ссылки или NTFS-соединения в вашем исходном дереве (чаще, чем вы думаете), или даже если у вас есть файл с тем же именем в другом системном каталоге include (это случалось со мной раньше, когда я версия 1 и версия 2 одной и той же библиотеки, установленной в двух разных системах, включают пути). Итог: я предпочитаю иметь больший контроль и жить с случайными ошибками программного обеспечения, чем доверять компилятору, который сделает это за меня. - person Thomi; 03.09.2009

#include guards - стандартный способ сделать это. #pragma once - нет, то есть не все компиляторы его поддерживают.

person Dima    schedule 03.09.2009

В моем проекте (я использую VS 2008 SP1) работает следующее решение:

Заголовочный файл:

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

Cpp класс:

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

где #include "winsock2class.h" означает класс, в котором реализован winsock2.h:

//winsock2class.h
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "Ws2_32.lib")
person Yahor M    schedule 31.10.2016

На самом деле я столкнулся с проблемой, когда мне пришлось определить winsock2.h как первое включение, похоже, у него есть другие проблемы с включениями из других пакетов. Надеюсь, это будет полезно для тех, кто сталкивается с той же проблемой, не только с windows.h, но и со всеми.

person Jeff    schedule 12.11.2018