O nadużywaniu C++/CLI

Odpowiedz Nowy wątek
2012-04-09 22:58
Rev
18

@Azarien napisał dzisiaj, że Nie ma powodu, by C++/CLI musiało być tylko do „portowania bibliotek” poza tym, że wielu tak uważa. Chciałbym poruszyć ten wątek, a że w tamtym w gruncie rzeczy mowa o innym języku to założyłem nowy.

Ostatnimi czasy widać w Polsce zwiększające się zainteresowanie językiem C++/CLI. Jak dla mnie, wynika to z niczego innego jak próbą pójścia na skróty w nauce czystego C++. Wiadomo: czytelniejsza biblioteka standardowa, a klepanie interfejsu użytkownika w Visual Studio nigdy nie było prostsze. Koniec końców najczęściej jest taki, że przychodzi człowiek na forum, bo nie może sobie poradzić ze składnią tego tworu i okazuje się, że nie poznał ani C++, ani .NET.

Także apeluję do młodszych (stażem, bo sam młody wiekiem jestem) programistów: nie zabierajcie się za C++/CLI, jeżeli chcecie się nauczyć C++ oraz nie zabierajcie się za C++/CLI, jeżeli chcecie nauczyć się .NET.

W pierwszym przypadku uczcie się po prostu C++. Po dogłębnym przestudiowaniu wcale niedużej biblioteki standardowej ze zdziwieniem stwierdzicie na koniec, że i tak zaoszczędzi wam kupę czasu, bo przykładowo nie trzeba już korzystać z ręcznie klepanych pętli czy mieszać gdzieś w kod bibliotekę C do operowania na napisach, bo nigdy wcześniej nie spodziewaliście się, że nagłówek <algorithm>, iteratory, insertery, i/o stringstreamy mogą być tak proste i przydatne. Można pójść dalej i poznać nowe (w standardzie) inteligentne wskaźniki i na zawsze zrezygnować z ręcznego zarządzania pamięci.

Jeżeli z kolei podoba wam się przeciąganie kontrolek na formę w Visual Studio i nie macie odgórnego wymogu (ze szkoły, uczelni) na naukę C++ to bezwzględnie piszcie od razu w C#. O ile na samiutkim początku może zbyt dużej różnicy nie ma, bo kompilator i tak wygeneruje kod za was, to już za chwilę zababracie się zupełnie w C++/CLI. Nie jest to wcale dziwne, bo nie dość, że C++ jest językiem skomplikowanym to dołączenie do niego zawiłości CLR z niedoskonałą (w porównaniu do C#, gdzie twórcy języka mogli zaprojektować go od zupełnych podstaw) składnią prowadzi do katastrofy. A idzie nam jeszcze Windows 8, WinRT i kolejna odmiana C++, czyli C++/CX z podobnymi, ale znów innymi i prowadzącymi do kolejnych nieporozumień rozszerzeniami.

Spójrzcie z resztą na tą bardzo prostą klasę, którą napisałem jakiś czas temu w C#.

namespace Rev.PhotoAlbum.Application.Modules
{
    using Autofac;
 
    using NHibernate;
    using NHibernate.Cfg;
 
    using Rev.PhotoAlbum.Model.Configuration;
    using Rev.PhotoAlbum.Model.Repositories;
 
    public class DataAccessModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            var configuration = Configurator.BuildConfiguration();
            var sessionFactory = Configurator.BuildSessionFactory(configuration);
 
            RegisterComponents(builder, configuration, sessionFactory);
        }
 
        private void RegisterComponents(ContainerBuilder builder, Configuration configuration, ISessionFactory sessionFactory)
        {
            builder.RegisterInstance(configuration).As<Configuration>().SingleInstance();
            builder.RegisterInstance(sessionFactory).As<ISessionFactory>().SingleInstance();
            builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).As<ISession>().InstancePerLifetimeScope();
 
            builder.RegisterAssemblyTypes(typeof(IRepository<>).Assembly)
                .Where(t => t.IsClosedTypeOf(typeof(Repository<,>)))
                .AsClosedTypesOf(typeof(IRepository<>));
        }
    }
}

Nie ma co prawda w kodzie komentarzy, ale dzięki odpowiedniej konstrukcji frameworków, z których korzystam kod ten można.. po prostu przeczytać - ciągiem. Układają się z tego zdania i na pierwszy rzut oka można się domyślić co się dzieje - chodzi mi zwłaszcza o metodę RegisterComponents. Jeżeli ktoś nigdy z AutoFaca nie korzystał, a ma minimalną wiedzę z teorii na temat kontenerów IoC mógłby własny kod napisać po jedno, dwu-krotnym przeczytaniu mojego.

A teraz ten sam kod w C++/CLI.
Najpierw oczywiście plik nagłówkowy, jako sumienni programiści C++ nie wrzucamy wszystkiego do jednego pliku.

#pragma once
 
namespace Rev
{
    namespace PhotoAlbum
    {
        namespace Application
        {
            namespace Modules
            {
                ref class DataAccessModule : public Autofac::Module
                {
                private:
                    void RegisterComponents(Autofac::ContainerBuilder^ builder, NHibernate::Cfg::Configuration^ configuration, NHibernate::ISessionFactory^ sessionFactory);
                protected:
                    virtual void Load(Autofac::ContainerBuilder^ builder) override;
                };
            }
        }
    }
}

Ah, no tak. C++ nie obsługuje zagnieżdżonych przestrzeń nazw w jednej instrukcji, musimy rozbić je na kilka. Tutaj pedantycznie trzymam się formatowania, które sobie przyjąłem, możemy zawsze wrzucić wszystkie namespace'y do jednej linijki albo - jak polecają w internecie - stworzyć sobie makro. A jak.
Rozciągnął się kod niesamowicie, no, ale przecież mamy na uwadze zasadę nieużywania using namespace w plikach nagłówkowych, żeby nie potworzyć żadnych konfliktów nazw w plikach, gdzie będziemy nasz plik nagłówkowy dołączać.

using namespace Rev::PhotoAlbum::Application::Modules;
 
using namespace Autofac;
 
using namespace NHibernate;
using namespace NHibernate::Cfg;
 
using namespace Rev::PhotoAlbum::Model::Configuration;
using namespace Rev::PhotoAlbum::Model::Repositories;
 
void DataAccessModule::Load(ContainerBuilder^ builder)
{
    auto configuration = Configurator::BuildConfiguration();
    auto sessionFactory = Configurator::BuildSessionFactory(configuration);
 
    RegisterComponents(builder, configuration, sessionFactory);
}
 
ISession^ ISessionResolve(IComponentContext^ context)
{
    return ResolutionExtensions::Resolve<ISessionFactory^>(context)->OpenSession();
}
 
bool IRepositoryWhereCondition(System::Type^ type)
{
    return TypeExtensions::IsClosedTypeOf(type, Repository::typeid);
}
 
void DataAccessModule::RegisterComponents(ContainerBuilder^ builder, Configuration^ configuration, ISessionFactory^ sessionFactory)
{
    RegistrationExtensions::RegisterInstance(builder, configuration)->As<Configuration^>()->SingleInstance();
    RegistrationExtensions::RegisterInstance(builder, sessionFactory)->As<ISessionFactory^>()->SingleInstance();
 
    RegistrationExtensions::Register<ISession^>(builder, gcnew System::Func<IComponentContext^, ISession^>(&ISessionResolve))->As<ISession^>()->InstancePerLifetimeScope();
 
    RegistrationExtensions::AsClosedTypesOf(RegistrationExtensions::Where(RegistrationExtensions::RegisterAssemblyTypes(builder, IRepository::typeid->Assembly),
        gcnew System::Func<System::Type^, bool>(&IRepositoryWhereCondition)), IRepository::typeid);
}

Chciałbym zwłaszcza zwrócić na końcówkę metody RegisterComponents. Konia z rzędem temu, kto rozszyfruje co się tutaj dzieje. Niestety (albo stety) bardzo wiele frameworków, które ułatwiają (taa) pisanie w .NET korzystają z metod rozszerzających (z resztą zdaje się, że bardzo dobrze, zgodnie z zasadą open/closed). Z jakiś powodów najzwyczajniej nie są one składniowo obsługiwane w C++/CLI i trzeba wywoływać je ręcznie, z klasy, w której są zdefiniowane i przekazywać ręcznie obiekt. Bez tracenia dodatkowego czasu na znalezienie chociażby nazwy tej klasy się nie obejdzie. Kolejna rzecz to lambdy, które w C# niesamowicie ułatwiają sprawę, a w C++/CLI powodują, że musimy je wydzielić do zewnętrznej metody. Lambdy z C++11 nawet w Visual Studio 11 nie są z nimi kompatybilne. No trudno.
Ktoś mógłby powiedzieć, że klepanie nawet takiego kodu z IntelliSense nie będzie trudne. Nawet dodali do niego obsługę C++/CLI. Bardziej przykra sprawa jest taka, że na co trzeciej bardziej rozbudowanej linijce z typami generycznymi po prostu się wykłada i zaczyna krzyczeć o błędach. Których de facto nie ma, bo kod się kompiluje i działa.

Wykorzystanie w C#:

var builder = new ContainerBuilder();
builder.RegisterModule<Modules.DataAccessModule>();
var container = builder.Build();
var repository = container.Resolve<IRepository<Person>>();

I C++/CLI:

auto builder = gcnew ContainerBuilder();
RegistrationExtensions::RegisterModule<Modules::DataAccessModule^>(builder);
auto container = builder->Build(Builder::ContainerBuildOptions::Default);
auto repository = ResolutionExtensions::Resolve<IRepository<Person^>^>(container);

Nie domyślił się wartości domyślnej parametru, a IntelliSense rzucił malowniczy błąd w ostatniej linii o tym, że wywołanie pasuje do wielu przeładowań oraz jednocześnie ma za mało parametrów.

Nie mówię, że pisanie w C++/CLI nie ma w ogóle sensu. Ma, ale są to naprawdę marginalne zastosowania, gdy nie wystarcza nam P/Invoke i interop w C#. Ja jeszcze nigdy nie musiałem go naprawdę użyć. Wy pewnie też nie.


To nie zawsze jest tak fajnie. Większość ludzi poznaje programowanie na studiach (bądź w TI - ale tu jest sporo godzin w cyklu kształcenia na PSiO i program nie jest zbyt rozbudowany. Jestem akurat świadkiem tego jak na Politechnice Poznańskiej wyglądają zajęcia z <i>programowani</i>. I semestr fizyk techniczna, w sześciu 90 minutowych wykładam omówiona całość składni C++ (włączywszy takie szczegóły jak wskaźniki na funkcje, wskaźniki na składowe, sporo funkcji z <code>cstring, stdio, stdlib</code>. No i super bo ludzie nic nie wiedzą, nic a wykładowca zadowolony. - szasza 2013-01-23 16:55
Na ćwiczeniach... prowadzący pisze kod, rzuca na ekran. 5 min na przepisanie i "to już wiecie z grubsza o co tutaj chodzi". Tyle w I sem. Na drugim roku, fizyka komputerowa. Po takich wątpliwych postawach C++, od razu rzut na C++/CLI. Gierki typu obsługa zdarzenia i poruszanie piłeczką aby trafić w uciekającą w losowe miejsca na ekranie bramkę. Metoda uczenia? "Otwieracie nowy projekt i na moim komputerze macie plik, z niego kopiujecie, jeśli działa to idziemy dalej". Jeśli nie działa to "szukacie błędów" a jak ludzie mają ich szukać jak nie rozumieją większości kodu... - szasza 2013-01-23 16:57

Pozostało 580 znaków

2012-04-10 14:04
1

co to znaczy "portowanie bibliotek"?


"Wszystko co może być wynalezione, zostało wynalezione"
Charless H. Duell - komisarz urzędu patentowego U.S., 1899

Pozostało 580 znaków

2012-04-10 14:36
0

Pewnie chodziło o wrappery.

A tak w temacie, to mam też wrażenie, że początkujący często nieświadomie piszą w C++/CLI. W końcu nazwa podobna:).

Pozostało 580 znaków

2012-04-10 15:33
0

Co się dziwić, znaczna część programistów (edit: tzn tych bardzo początkujących) C# myśli, że (C#) to jest jakiś następca C++.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit, 2012-04-10 22:53

Pozostało 580 znaków

2012-04-10 16:30
msm
0

@Rev - myślę że problem z twoją argumentacją wynika z tego że próbujesz patrzeć na C++/CLI jak na C# z inną składnią. Chociażby AutoFac którego przykład podałeś - był on projektowany specjalnie pod C#. To że w ogóle działa pod C++/CLI to wynik zgodności języków zarządzanych a nie jakieś celowe zagranie.

Równie kiepskie podejście do C++/CLI to patrzenie na niego jako na C++ z dodatkową biblioteką standardową. Momentalnie zaczynają pojawiać się problemy z łączeniem zarządzanych i niezarządzanych fragmentów.

Pozostało 580 znaków

2012-04-10 18:27
3
Wibowit napisał(a)

Co się dziwić, znaczna część programistów C# myśli, że (C#) to jest jakiś następca C++.

Tja, a programiści JavaScript myślą, że to jakiś następca Javy.
Jeśli ktoś tak uważa, to programistą C# nie jest i raczej nie będzie.

Tezcatlipoca napisał(a)

@Rev - myślę że problem z twoją argumentacją wynika z tego że próbujesz patrzeć na C++/CLI jak na C# z inną składnią.

Nie zauważyłem, żeby @Rev miał problem z argumentacją, to chyba Ty masz jakiś problem z jego argumentami. To nie on patrzy na C++/CLI w ten sposób, lecz ci, którzy próbują go tak używać i na siłę robić z niego język ogólnego przeznaczenia.

Jakieś dziecko przeczyta w internecie, że Prawdziwi Profesjonalni Programiści piszą w C++, ściąga VC++, tworzy projekt i zaczyna się magia, dzięki której w 5 minut robi GUI jak w prawdziwym programie, więc myśli, że dalej będzie już z górki. Dzieciak nie wie, że to nie jest C++, dzieciak nie wie o co chodzi z tym CLI, nie wie, co to .NET - nie wie nic, ale myśli, że uczy się Jedynego Słusznego Języka Do Pisania Gier. I stąd wynikają wspomniane przez Ciebie problemy z łączeniem zarządzanych i niezarządzanych fragmentów kodu.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

2012-04-10 20:00
msm
1
somekind napisał(a)
Tezcatlipoca napisał(a)

@Rev - myślę że problem z twoją argumentacją wynika z tego że próbujesz patrzeć na C++/CLI jak na C# z inną składnią.

Nie zauważyłem, żeby @Rev miał problem z argumentacją, to chyba Ty masz jakiś problem z jego argumentami. To nie on patrzy na C++/CLI w ten sposób, lecz ci, którzy próbują go tak używać i na siłę robić z niego język ogólnego przeznaczenia.

Ja tylko zauważam że porównanie C++/CLI i C# na podstawie przepisywania kodu z tego drugiego do pierwszego jest trochę nieodpowiednie.
Te języki mają po prostu zupełnie inne zastosowania i to jest chyba dla wszystkich oczywiste?

edytowany 2x, ostatnio: msm, 2012-04-10 20:00

Pozostało 580 znaków

2012-04-10 20:26
Rev
1

Te języki mają po prostu zupełnie inne zastosowania i to jest chyba dla wszystkich oczywiste?

W całym moim wywodzie chodzi właśnie o to, żeby to pokazać. Gdybyś czytał wszystkie wątki w dziale .NET i C++ to wiedziałbyś, że jest to sprawa wielce nieoczywista dla niezliczonej ilości osób.
A ten nieudany kod w C++/CLI, który zaprezentowałem tylko to potwierdza. Ten przykład miał pokazać, że pisanie kodu aplikacji biznesowych w środowisku .NET w C++/CLI jest bez sensu, gdy mamy C#.


Pozostało 580 znaków

2012-04-10 20:36
0

Zawsze mi się wydawało, że głównym celem C++/CLI jest ułatwienie przeniesienia sporych aplikacji na platformę .NET bez potrzeby przepisywania całego kodu do C#, tylko pisząc w nim nowe elementy.

Pozostało 580 znaków

2012-04-10 21:50
0
Tezcatlipoca napisał(a)

Ja tylko zauważam że porównanie C++/CLI i C# na podstawie przepisywania kodu z tego drugiego do pierwszego jest trochę nieodpowiednie.

A na jakiej innej zasadzie można porównać przydatność języków do danego zastosowania, jeśli nie poprzez zwięzłość i czytelność kodu?


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

2012-04-10 22:20
0
somekind napisał(a)
Wibowit napisał(a)

Co się dziwić, znaczna część programistów C# myśli, że (C#) to jest jakiś następca C++.

Tja, a programiści JavaScript myślą, że to jakiś następca Javy.
Jeśli ktoś tak uważa, to programistą C# nie jest i raczej nie będzie.

Przecież na tym forum niejeden początkujący programista C# tak myśli...

A na jakiej innej zasadzie można porównać przydatność języków do danego zastosowania, jeśli nie poprzez zwięzłość i czytelność kodu?

Haskell jest zwięzły, czytelny, a obecne wersje kompilatorów tworzą dość wydajny kod.

Tak mogą myśleć jacyś newbie i totalni laicy, a nie ktoś, kto ma jako takie pojęcie o tych językach. :)

No napisałem początkujący (czyli newbie). Zbieżność nazw C++ i C# jest dla nich argumentem za tym, żeby po C++ wybrać C#, a nie np Javę, Scalę, Ruby, Pythona, etc Podobnie w drugą stronę, był tu przypadek typa, który myślał, że C++ to taki inny C# i wrzucał public i private przed każdą deklaracją.

stfu:
Zgoda, zapomniałem dopisać początkujących.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit, 2012-04-10 22:54
Tak mogą myśleć jacyś newbie i totalni laicy, a nie ktoś, kto ma jako takie pojęcie o tych językach. :) - somekind 2012-04-10 22:45
Znaczna część programistów != niejeden początkujący programista (na tym forum) - stfu 2012-04-10 22:50
Typowe zachowanie piekarza który wstał rano z wyra lewą nogą i głośno powiedział: od dziś będę programistą. Nie zmienia to jednak faktu iż to nie jego wina że na rynku programowania mamy burdel. C,C#,C++,VC#,VC++... Do tego C99, C11 i masa innych. ANSI trochę przegina. - tubiz 2017-06-09 06:42

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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