Geliştirme sırasında farklı davranış türleri arasında geçiş yapmak için #ifdef kullanımı


28

Farklı davranış türleri arasında geçiş yapmak için geliştirme sırasında #ifdef kullanmak iyi bir uygulama mıdır? Örneğin, var olan kodun davranışını değiştirmek istiyorum, davranışı nasıl değiştireceğime dair birkaç fikrim var ve farklı yaklaşımları test etmek ve karşılaştırmak için farklı uygulamalar arasında geçiş yapmak gerekiyor. Genellikle koddaki değişiklikler karmaşıktır ve farklı dosyalardaki farklı yöntemler üzerinde etkili olur.

Genelde birkaç tanımlayıcı tanıtırım ve böyle bir şey yaparım.

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

void bar()
{
    doSomething3();
#ifndef APPROACH3
    doSomething4();
#endif
    doSomething5();
#ifdef APPROACH2
    bar_approach2();
#endif
}

int main()
{
    foo();
    bar();
    return 0;
}

Bu, farklı yaklaşımlar arasında hızlıca geçiş yapmanıza ve kaynak kodun yalnızca bir kopyasında her şeyi yapmanıza olanak sağlar. Gelişim için iyi bir yaklaşım mı yoksa daha iyi bir uygulama mı var?



2
Gelişimden bahsettiğinizden, farklı uygulamaları değiştirip denemek için yapabileceğiniz her şeyi yapmanız gerektiğine inanıyorum. Bu, gelişim sırasında kişisel tercihler gibidir, belirli bir sorunu çözmek için en iyi uygulamalar değildir.
Emerson Cardoso

1
Değiştirilebilir davranış için tek bir eklenti noktası tutmaya yardımcı olduğundan, strateji modelini veya iyi ol polimorfizmini kullanmanızı öneririm.
pmf

4
Bazı IDE'lerin #ifdefblok kapalıyken bloklardaki hiçbir şeyi değerlendirmediğini unutmayın . Tüm yolları düzenli olarak oluşturmazsanız, kodun kolayca eskimeye neden olabileceği ve derleme yapamayacağı durumlar ile karşılaştık.
Berin Loritsch

Başka bir soruya verdiğim cevaba bir bakın . Daha #ifdefsaz hantal hale getirmek için bazı yollar ortaya koymaktadır .
user1118321

Yanıtlar:


9

Bu kullanım durumu için versiyon kontrol dallarını kullanmayı tercih ederim. Bu, uygulamalar arasında farklılık göstermenize, her biri için ayrı bir geçmiş tutmanıza ve kararınızı verdiğinizde ve sürümlerden birini kaldırmanız gerektiğinde, yalnızca bu dalı hataya açık bir düzenlemeden geçmek yerine atmanız gerekir.


gitbu tür şeylerde özellikle usta. Belki çok değil durum svn, hgveya başkalarının, ama yine de yapılabilir.
twalberg

Bu benim de ilk düşüncemdi. "Farklı bir şeyle uğraşmak ister misin?" git branch!
Wes Toleman

42

Bir çekiç tutarken, her şey bir çiviye benziyor. #ifdefProgramınızda özel davranışlar elde etmenin bir yolu olarak nasıl kullanılacağını bir kez öğrendiğinizde cazip gelir. Biliyorum çünkü aynı hatayı ben yaptım.

MFC C ++ ile yazılmış, zaten #ifdefplatforma özel değerleri tanımlamak için kullanılan eski bir programı miras aldım . Bu, programımı 32-bitlik bir platformda veya 64-bitlik bir platformda kullanılmak üzere sadece belirli makro değerlerini tanımlayarak (veya bazı durumlarda tanımlamamak suretiyle) derleyebileceğim anlamına geliyordu.

Sorun sonra bir müşteri için özel davranış yazmak için gerekli olduğu ortaya çıktı. Bir şube oluşturabilir ve müşteri için ayrı bir kod tabanı oluşturabilirdim, ancak bu bir bakım cehennemi olurdu. Başlangıçta program tarafından okunacak konfigürasyon değerlerini de tanımlayabilir ve davranışları belirlemek için bu değerleri kullanabilirdim, ancak daha sonra her istemcinin konfigürasyon dosyalarına uygun konfigürasyon değerlerini eklemek için özel kurulumlar oluşturmam gerekirdi.

Baştan çıkarıldım ve verdim. #ifdefÇeşitli davranışları ayırt etmek için kodumda bölümler yazdım . Hata yapma, ilk başta en üstte hiçbir şey yoktu. Programın sürümlerini müşterilerime yeniden dağıtmama izin veren çok küçük davranış değişiklikleri yapıldı ve kod tabanının birden fazla sürümüne ihtiyacım yok.

Zamanla bu yine de bakım cehennemi haline geldi çünkü program artık pano boyunca sürekli davranmıyordu. Programın bir sürümünü test etmek istersem müşterinin kim olduğunu bilmek zorunda kaldım. Kod, bir veya iki başlık dosyasına düşürmeyi denesem de, çok karışıktı ve sağlanan hızlı düzeltme yaklaşımı, #ifdefkötü huylu bir kanser gibi program boyunca yayılan çözümler anlamına geliyordu.

O zamandan beri dersimi öğrendim ve sen de öğrenmelisin. Kesinlikle yapmanız gerekiyorsa kullanın ve platform değişiklikleri için kesinlikle kullanın. Programlar (ve dolayısıyla istemciler) arasındaki davranış farklılıklarına yaklaşmanın en iyi yolu, yalnızca başlangıçta yüklenen yapılandırmayı değiştirmektir. Program tutarlı kalıyor ve hem okunması hem de hata ayıklaması kolaylaşıyor.


"debug, x değişkenini tanımla ..." gibi hata ayıklama sürümleri ne olacak? Bu, günlüğe kaydetme gibi şeyler için yararlı olabilir gibi görünüyor, ancak daha sonra hata ayıklama etkin olduğunda ve olmadığı zaman programınızın çalışma şeklini de tamamen değiştirebilir .
whn

8
@snb hakkında düşündüm. Bir yapılandırma dosyasını değiştirmeyi ve daha ayrıntılı bir şekilde günlüğe kaydetmeyi tercih ediyorum. Aksi halde, programın yapım aşamasında bir şeyler ters gider ve çalıştırılabilir dosyayı tamamen değiştirmeden programın hatalarını ayıklama imkanınız yoktur. İdeal durumlarda bile, bu istenenden daha azdır. ;)
Neil

Oh evet, hata ayıklama için yeniden derlemek zorunda kalmamak daha ideal olurdu, bunu düşünmedim!
whn

9
Neyi tarif ettiğinize aşırı bir örnek için, MS'in neden birkaç yıl önce C çalışma zamanlarının çoğunu sıfırdan yeniden yazmak zorunda kaldıkları noktaya ulaştığı konusundaki "Bakım Kolaylığı Problemi" alt başlığının altındaki 2. paragrafa bakın. . blogs.msdn.microsoft.com/vcblog/2014/06/10/…
Dan

2
@snb Günlüğe kaydetme kitaplıklarının çoğu, günlük kaydı düzeyinde bir mekanizma olduğunu varsayar. Hata ayıklama sırasında belirli bilgilerin kaydedilmesini istiyorsanız, düşük bir kayıt seviyesiyle (genellikle "Hata Ayıklama" veya "Verbose") giriş yapın. Sonra uygulama, hangi seviyenin günlüğe kaydedileceğini söyleyen bir yapılandırma parametresine sahiptir. Yani cevap hala bu problem için konfigürasyondur. Bu aynı zamanda, müşteri ortamında bu düşük kayıt seviyesini açabilmenin muazzam yararına sahiptir .
jpmc26

21

Geçici olarak ne yaptığınızla ilgili yanlış bir şey yoktur (check-in işleminden önce): Farklı teknik kombinasyonlarını test etmek veya bir kod bölümünü yok saymak için (kendi başındaki problemleri konuşsa da) harika bir yol.

Ama uyarı bir kelime: şubesi #ifdef tutmuyorum aynı şey sadece dışarı rakama, dört farklı şekilde uygulanan okuma benim vakit biraz daha sinir bozucu olduğunu ben okuma olmalıdır hangisi .

Bir #ifdef üzerinden okumak, atlamak için gerçekten hatırlamanız gerektiği için çaba harcar! Kesinlikle olması gerekenden daha zorlaştırmayın.

#İfdefs'i olabildiğince az kullanın. Bunu, geliştirme ortamınızda Debug / Release sürümleri gibi kalıcı farklar veya farklı mimariler için yapmanın genellikle yolları vardır .

#İfdef bölmelerini gerektiren kütüphane sürümlerine bağlı kütüphane özellikleri yazdım. Bu yüzden bazen tek yol ya da en kolay yol olabilir, ama o zaman bile onları tuttuğunuz için üzülmelisiniz.


1

Bu şekilde #ifdefs kullanmak kodu okumayı çok zorlaştırır.

Yani, hayır, böyle #ifdefs kullanmayın.

Neden ifdefs kullanmamayacağınız bir sürü argüman olabilir, bu benim için yeterli.

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

Yapabileceği bir çok şey yapabilir:

void foo()
{
    doSomething1();
    doSomething2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
}

void foo()
{
    doSomething1();
    doSomething2();
    foo_approach2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
    foo_approach2();
}

Tüm bunlar hangi yaklaşımların tanımlandığına veya tanımlanmadığına bağlı olarak Ne yaptığını ilk bakışta kesinlikle belli değil.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.